From 88c21d1828847bc167e18a92fdb2dc7ddc579322 Mon Sep 17 00:00:00 2001 From: ycc Date: Fri, 3 Mar 2023 14:43:11 +0100 Subject: [PATCH] initial commit --- .dockerignore | 4 + .gitignore | 34 +- Dockerfile | 59 + README.md | 25 +- conf/app.conf | 17 + controllers/computing.go | 77 + controllers/data.go | 85 + controllers/datacenter.go | 82 + controllers/schedule.go | 163 ++ controllers/search.go | 31 + controllers/storage.go | 77 + controllers/tokens.go | 68 + controllers/user.go | 54 + controllers/workflow.go | 337 +++ controllers/workspace.go | 151 ++ docker-compose.backend.yml | 15 + docker-compose.multi.yml | 27 + docker-compose.yml | 30 + go.mod | 32 + go.sum | 747 ++++++ main.go | 31 + models/computing.go | 164 ++ models/data.go | 125 ++ models/datacenter.go | 212 ++ models/generic.go | 118 + models/mxgraph.go | 35 + models/rtype/rtype.go | 73 + models/schedule.go | 322 +++ models/search.go | 101 + models/storage.go | 138 ++ models/user.go | 12 + models/workflow.go | 997 ++++++++ models/workspace.go | 418 ++++ routers/auth.go | 118 + routers/router.go | 129 ++ routers/uglyfix.go | 42 + scripts/demo.json | 322 +++ scripts/demo_dc2.json | 135 ++ scripts/demo_dc3.json | 135 ++ scripts/generate_selfapi.sh | 15 + scripts/local_imgs/CNES datacenter.png | Bin 0 -> 15589 bytes scripts/local_imgs/Environment builder.png | Bin 0 -> 39993 bytes .../local_imgs/Fire propagation simulator.png | Bin 0 -> 14744 bytes .../Flammable vegetation slicer.png | Bin 0 -> 42893 bytes scripts/local_imgs/IRT local file storage.png | Bin 0 -> 47625 bytes scripts/local_imgs/IRT risk database.png | Bin 0 -> 18637 bytes ...Long term fire risk mitigation planner.png | Bin 0 -> 14744 bytes .../local_imgs/Meteo France datacenter.png | Bin 0 -> 38419 bytes scripts/local_imgs/Meteo-France forecasts.png | Bin 0 -> 26666 bytes .../local_imgs/Meteo-France wind archive.png | Bin 0 -> 33697 bytes .../Mundi Sentienl 3 OLCI Images.png | Bin 0 -> 20266 bytes .../Mundi Sentienl 3 SRAL Images.png | Bin 0 -> 20375 bytes scripts/local_imgs/Mundi datacenter.png | Bin 0 -> 14735 bytes scripts/local_imgs/SAR High points.png | Bin 0 -> 16300 bytes scripts/populate_models.sh | 62 + selfapi/.gitignore | 24 + selfapi/.swagger-codegen-ignore | 23 + selfapi/.swagger-codegen/VERSION | 1 + selfapi/.travis.yml | 8 + selfapi/README.md | 100 + selfapi/api/swagger.yaml | 1996 +++++++++++++++++ selfapi/api_computing.go | 269 +++ selfapi/api_data.go | 269 +++ selfapi/api_datacenter.go | 269 +++ selfapi/api_schedule.go | 465 ++++ selfapi/api_search.go | 113 + selfapi/api_storage.go | 269 +++ selfapi/api_user.go | 160 ++ selfapi/api_workflow.go | 901 ++++++++ selfapi/api_workspace.go | 334 +++ selfapi/client.go | 499 +++++ selfapi/configuration.go | 73 + selfapi/docs/ComputingApi.md | 95 + selfapi/docs/DataApi.md | 95 + selfapi/docs/DatacenterApi.md | 95 + selfapi/docs/ModelsComputingModel.md | 20 + selfapi/docs/ModelsComputingNewModel.md | 19 + selfapi/docs/ModelsComputingObject.md | 13 + selfapi/docs/ModelsDCstatus.md | 15 + selfapi/docs/ModelsDataModel.md | 19 + selfapi/docs/ModelsDataNewModel.md | 18 + selfapi/docs/ModelsDataObject.md | 10 + selfapi/docs/ModelsDatacenterCpuModel.md | 14 + selfapi/docs/ModelsDatacenterGpuModel.md | 13 + selfapi/docs/ModelsDatacenterMemoryModel.md | 11 + selfapi/docs/ModelsDatacenterModel.md | 22 + selfapi/docs/ModelsDatacenterNewModel.md | 21 + selfapi/docs/ModelsDatacenterObject.md | 10 + .../docs/ModelsExecutionRequirementsModel.md | 15 + selfapi/docs/ModelsRepositoryModel.md | 11 + selfapi/docs/ModelsScheduleDb.md | 13 + selfapi/docs/ModelsScheduleInfo.md | 11 + selfapi/docs/ModelsScheduleTime.md | 9 + selfapi/docs/ModelsSearchResult.md | 13 + selfapi/docs/ModelsStorageModel.md | 21 + selfapi/docs/ModelsStorageNewModel.md | 20 + selfapi/docs/ModelsStorageObject.md | 12 + selfapi/docs/ModelsWorkflow.md | 15 + selfapi/docs/ModelsWorkflowSchedule.md | 16 + selfapi/docs/ModelsWorkspace.md | 15 + selfapi/docs/ModelsWorkspaceModel.md | 14 + selfapi/docs/PrimitiveObjectId.md | 9 + selfapi/docs/ScheduleApi.md | 164 ++ selfapi/docs/SearchApi.md | 37 + selfapi/docs/StorageApi.md | 95 + selfapi/docs/TimeTime.md | 9 + selfapi/docs/UserApi.md | 63 + selfapi/docs/WorkflowApi.md | 345 +++ selfapi/docs/WorkspaceApi.md | 118 + selfapi/git_push.sh | 52 + selfapi/model_models_computing_model.go | 26 + selfapi/model_models_computing_new_model.go | 25 + selfapi/model_models_computing_object.go | 20 + selfapi/model_models_d_cstatus.go | 20 + selfapi/model_models_data_model.go | 27 + selfapi/model_models_data_new_model.go | 26 + selfapi/model_models_data_object.go | 16 + selfapi/model_models_datacenter_cpu_model.go | 19 + selfapi/model_models_datacenter_gpu_model.go | 19 + .../model_models_datacenter_memory_model.go | 17 + selfapi/model_models_datacenter_model.go | 29 + selfapi/model_models_datacenter_new_model.go | 28 + selfapi/model_models_datacenter_object.go | 16 + ...del_models_execution_requirements_model.go | 22 + selfapi/model_models_repository_model.go | 16 + selfapi/model_models_schedule_db.go | 18 + selfapi/model_models_schedule_info.go | 16 + selfapi/model_models_schedule_time.go | 14 + selfapi/model_models_search_result.go | 18 + selfapi/model_models_storage_model.go | 27 + selfapi/model_models_storage_new_model.go | 26 + selfapi/model_models_storage_object.go | 18 + selfapi/model_models_workflow.go | 21 + selfapi/model_models_workflow_schedule.go | 23 + selfapi/model_models_workspace.go | 20 + selfapi/model_models_workspace_model.go | 19 + selfapi/model_primitive_object_id.go | 14 + selfapi/model_time_time.go | 14 + selfapi/response.go | 44 + services/discovery.go | 35 + services/init.go | 28 + services/mongo.go | 157 ++ 142 files changed, 13975 insertions(+), 22 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 conf/app.conf create mode 100644 controllers/computing.go create mode 100644 controllers/data.go create mode 100644 controllers/datacenter.go create mode 100644 controllers/schedule.go create mode 100644 controllers/search.go create mode 100644 controllers/storage.go create mode 100644 controllers/tokens.go create mode 100644 controllers/user.go create mode 100644 controllers/workflow.go create mode 100644 controllers/workspace.go create mode 100644 docker-compose.backend.yml create mode 100644 docker-compose.multi.yml create mode 100644 docker-compose.yml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 models/computing.go create mode 100644 models/data.go create mode 100644 models/datacenter.go create mode 100644 models/generic.go create mode 100644 models/mxgraph.go create mode 100644 models/rtype/rtype.go create mode 100644 models/schedule.go create mode 100644 models/search.go create mode 100644 models/storage.go create mode 100644 models/user.go create mode 100644 models/workflow.go create mode 100644 models/workspace.go create mode 100644 routers/auth.go create mode 100644 routers/router.go create mode 100644 routers/uglyfix.go create mode 100644 scripts/demo.json create mode 100644 scripts/demo_dc2.json create mode 100644 scripts/demo_dc3.json create mode 100755 scripts/generate_selfapi.sh create mode 100644 scripts/local_imgs/CNES datacenter.png create mode 100644 scripts/local_imgs/Environment builder.png create mode 100644 scripts/local_imgs/Fire propagation simulator.png create mode 100644 scripts/local_imgs/Flammable vegetation slicer.png create mode 100644 scripts/local_imgs/IRT local file storage.png create mode 100644 scripts/local_imgs/IRT risk database.png create mode 100644 scripts/local_imgs/Long term fire risk mitigation planner.png create mode 100644 scripts/local_imgs/Meteo France datacenter.png create mode 100644 scripts/local_imgs/Meteo-France forecasts.png create mode 100644 scripts/local_imgs/Meteo-France wind archive.png create mode 100644 scripts/local_imgs/Mundi Sentienl 3 OLCI Images.png create mode 100644 scripts/local_imgs/Mundi Sentienl 3 SRAL Images.png create mode 100644 scripts/local_imgs/Mundi datacenter.png create mode 100644 scripts/local_imgs/SAR High points.png create mode 100755 scripts/populate_models.sh create mode 100644 selfapi/.gitignore create mode 100644 selfapi/.swagger-codegen-ignore create mode 100644 selfapi/.swagger-codegen/VERSION create mode 100644 selfapi/.travis.yml create mode 100644 selfapi/README.md create mode 100644 selfapi/api/swagger.yaml create mode 100644 selfapi/api_computing.go create mode 100644 selfapi/api_data.go create mode 100644 selfapi/api_datacenter.go create mode 100644 selfapi/api_schedule.go create mode 100644 selfapi/api_search.go create mode 100644 selfapi/api_storage.go create mode 100644 selfapi/api_user.go create mode 100644 selfapi/api_workflow.go create mode 100644 selfapi/api_workspace.go create mode 100644 selfapi/client.go create mode 100644 selfapi/configuration.go create mode 100644 selfapi/docs/ComputingApi.md create mode 100644 selfapi/docs/DataApi.md create mode 100644 selfapi/docs/DatacenterApi.md create mode 100644 selfapi/docs/ModelsComputingModel.md create mode 100644 selfapi/docs/ModelsComputingNewModel.md create mode 100644 selfapi/docs/ModelsComputingObject.md create mode 100644 selfapi/docs/ModelsDCstatus.md create mode 100644 selfapi/docs/ModelsDataModel.md create mode 100644 selfapi/docs/ModelsDataNewModel.md create mode 100644 selfapi/docs/ModelsDataObject.md create mode 100644 selfapi/docs/ModelsDatacenterCpuModel.md create mode 100644 selfapi/docs/ModelsDatacenterGpuModel.md create mode 100644 selfapi/docs/ModelsDatacenterMemoryModel.md create mode 100644 selfapi/docs/ModelsDatacenterModel.md create mode 100644 selfapi/docs/ModelsDatacenterNewModel.md create mode 100644 selfapi/docs/ModelsDatacenterObject.md create mode 100644 selfapi/docs/ModelsExecutionRequirementsModel.md create mode 100644 selfapi/docs/ModelsRepositoryModel.md create mode 100644 selfapi/docs/ModelsScheduleDb.md create mode 100644 selfapi/docs/ModelsScheduleInfo.md create mode 100644 selfapi/docs/ModelsScheduleTime.md create mode 100644 selfapi/docs/ModelsSearchResult.md create mode 100644 selfapi/docs/ModelsStorageModel.md create mode 100644 selfapi/docs/ModelsStorageNewModel.md create mode 100644 selfapi/docs/ModelsStorageObject.md create mode 100644 selfapi/docs/ModelsWorkflow.md create mode 100644 selfapi/docs/ModelsWorkflowSchedule.md create mode 100644 selfapi/docs/ModelsWorkspace.md create mode 100644 selfapi/docs/ModelsWorkspaceModel.md create mode 100644 selfapi/docs/PrimitiveObjectId.md create mode 100644 selfapi/docs/ScheduleApi.md create mode 100644 selfapi/docs/SearchApi.md create mode 100644 selfapi/docs/StorageApi.md create mode 100644 selfapi/docs/TimeTime.md create mode 100644 selfapi/docs/UserApi.md create mode 100644 selfapi/docs/WorkflowApi.md create mode 100644 selfapi/docs/WorkspaceApi.md create mode 100644 selfapi/git_push.sh create mode 100644 selfapi/model_models_computing_model.go create mode 100644 selfapi/model_models_computing_new_model.go create mode 100644 selfapi/model_models_computing_object.go create mode 100644 selfapi/model_models_d_cstatus.go create mode 100644 selfapi/model_models_data_model.go create mode 100644 selfapi/model_models_data_new_model.go create mode 100644 selfapi/model_models_data_object.go create mode 100644 selfapi/model_models_datacenter_cpu_model.go create mode 100644 selfapi/model_models_datacenter_gpu_model.go create mode 100644 selfapi/model_models_datacenter_memory_model.go create mode 100644 selfapi/model_models_datacenter_model.go create mode 100644 selfapi/model_models_datacenter_new_model.go create mode 100644 selfapi/model_models_datacenter_object.go create mode 100644 selfapi/model_models_execution_requirements_model.go create mode 100644 selfapi/model_models_repository_model.go create mode 100644 selfapi/model_models_schedule_db.go create mode 100644 selfapi/model_models_schedule_info.go create mode 100644 selfapi/model_models_schedule_time.go create mode 100644 selfapi/model_models_search_result.go create mode 100644 selfapi/model_models_storage_model.go create mode 100644 selfapi/model_models_storage_new_model.go create mode 100644 selfapi/model_models_storage_object.go create mode 100644 selfapi/model_models_workflow.go create mode 100644 selfapi/model_models_workflow_schedule.go create mode 100644 selfapi/model_models_workspace.go create mode 100644 selfapi/model_models_workspace_model.go create mode 100644 selfapi/model_primitive_object_id.go create mode 100644 selfapi/model_time_time.go create mode 100644 selfapi/response.go create mode 100644 services/discovery.go create mode 100644 services/init.go create mode 100644 services/mongo.go diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..aab4195 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +Dockerfile +swagger +docker-compose.* +routers/commentsRouter* \ No newline at end of file diff --git a/.gitignore b/.gitignore index adf8f72..98910be 100644 --- a/.gitignore +++ b/.gitignore @@ -1,23 +1,15 @@ -# ---> Go -# If you prefer the allow list template instead of the deny list, see community template: -# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore -# -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib +lastupdate.tmp +oc-catalog +swagger +routers/commentsRouter* +.vscode +main +selfapi_bak -# Test binary, built with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out - -# Dependency directories (remove the comment below to include it) -# vendor/ - -# Go workspace file -go.work +# Scripts should contain isolated data models to populate. Just one file will be used as demo ref +scripts/* +!scripts/populate_models.sh +!scripts/demo*.json +!scripts/local_imgs +!scripts/generate_selfapi.sh \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c76708d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,59 @@ +FROM golang as builder + +LABEL maintainer="Valentin KIVACHUK BURDA" + +ENV DOCKER_ENVIRONMENT=true +ENV CGO_ENABLED=0 +ENV GOOS=linux +ENV GO111MODULE=on + +EXPOSE 49618 + +WORKDIR /go/src/oc-catalog + +####################################################### +RUN go get github.com/beego/bee/v2 + +# Manually download swagger during build +RUN ["/bin/bash", "-c", \ + "set -eo pipefail; \ + mkdir -p swagger; \ + curl -sL https://github.com/beego/swagger/archive/v3.tar.gz | tar xvvvz --overwrite -C swagger --strip-components=1"] + + +COPY go.mod . +COPY go.sum . +RUN go mod download -x + +# COPY . . +COPY main.go go.mod go.sum ./ + +COPY controllers controllers +COPY models models +COPY routers routers +COPY selfapi selfapi +COPY services services +COPY conf conf +COPY scripts scripts + +# RUN go build -a -tags netgo -ldflags '-w -extldflags "-static"' -installsuffix cgo . + +RUN bee generate docs + +# COPY . . + + +# FROM golang + +# WORKDIR /go/src/oc-catalog + +# COPY --from=builder /go/src/oc-catalog . + +ENV DOCKER_ENVIRONMENT=true + +RUN go build . + +# UglyFix: Generate comments from swagger +RUN timeout 10 bee run -runargs test || exit 0 + +CMD [ "bee", "run", "-gendoc=true" ] \ No newline at end of file diff --git a/README.md b/README.md index 859b2f2..ccfb89a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,25 @@ -# oc-catalog +# OC Catalog +OpenCloud Catalog API + + +## Full deploy + +Deploy with docker: +`docker-compose -f docker-compose.yml -f docker-compose.backend.yml up --build` + +and populate DB (or other scripts) with: +`docker exec -it oc-catalog_oc-catalog_1 ./scripts/populate_models.sh ./scripts/demo.json` + +## Dev + +Start DB with `docker-compose up -d` and run the API with `bee run -downdoc=true -gendoc=true` + + +## Multinode + +Deploy +`docker-compose -f docker-compose.yml -f docker-compose.backend.yml -f docker-compose.multi.yml up --build` + +Populate +`./scripts/multinode.sh ./scripts/demo.json` \ No newline at end of file diff --git a/conf/app.conf b/conf/app.conf new file mode 100644 index 0000000..23ca3c0 --- /dev/null +++ b/conf/app.conf @@ -0,0 +1,17 @@ +appname = oc-catalog +httpport = 49618 +runmode = dev +autorender = false +copyrequestbody = true + +EnableDocs = true +SessionOn = true + +DCNAME = "DC_myDC" +DBPOINT = "demo_06042021" + +[mongodb] +url = mongodb://127.0.0.1:27017/beego-demo + +[mongodb_docker] +url = mongodb://mongo:27017/beego-demo diff --git a/controllers/computing.go b/controllers/computing.go new file mode 100644 index 0000000..4cb722d --- /dev/null +++ b/controllers/computing.go @@ -0,0 +1,77 @@ +package controllers + +import ( + "cloud.o-forge.io/core/oc-catalog/models" + + beego "github.com/beego/beego/v2/server/web" +) + +// All operations related to the rType computing +type ComputingController struct { + beego.Controller +} + +// @Title Get computing by ID +// @Description Find a computing resource based on ID +// @Param ID path string true "The ID of the resource" +// @Success 200 {object} models.ComputingModel +// @Failure 403 ID is empty +// @router /:ID [get] +func (o *ComputingController) GetOneComputing(ID string) { + if ID != "" { + ob, err := models.GetOneComputing(ID) + if err != nil { + o.Data["json"] = err.Error() + } else { + o.Data["json"] = ob + } + } + o.ServeJSON() +} + +// @Title Add computing +// @Description Submit a computing object +// @Param body body models.ComputingNEWModel true "The object content" +// @Success 200 {string} ID +// @Failure 403 Missing body or fields +// @router / [post] +func (o *ComputingController) PostComputing(body models.ComputingNEWModel) { + err := validate.Struct(body) + // validationErrors := err.(validator.ValidationErrors) + + if err != nil { + o.Data["json"] = err.Error() + o.Ctx.Output.Status = 403 + o.ServeJSON() + return + } + + ID, err := models.PostOneComputing(body) + if err != nil { + o.Ctx.Output.SetStatus(500) + return + } + + o.Data["json"] = map[string]string{"ID": ID} + o.ServeJSON() +} + +// @Title Get multiple computing by IDs +// @Description Return Computing objects if found in the DB. Not found IDs will be ignored +// @Param IDs path []string true "List of computing IDs" +// @Success 200 {object} []models.ComputingModel +// @Failure 403 IDs are empty +// @router /multi/:IDs [get] +func (o *ComputingController) GetMultipleComputing(IDs []string) { + if len(IDs) != 0 { + ob, err := models.GetMultipleComputing(IDs) + if err != nil { + o.Ctx.Output.SetStatus(500) + } else { + o.Data["json"] = ob + } + } else { + o.Ctx.Output.SetStatus(403) + } + o.ServeJSON() +} diff --git a/controllers/data.go b/controllers/data.go new file mode 100644 index 0000000..5002877 --- /dev/null +++ b/controllers/data.go @@ -0,0 +1,85 @@ +package controllers + +import ( + "cloud.o-forge.io/core/oc-catalog/models" + + beego "github.com/beego/beego/v2/server/web" + "github.com/go-playground/validator/v10" +) + +// All operations related to the rType data +type DataController struct { + beego.Controller +} + +var validate *validator.Validate + +func init() { + validate = validator.New() +} + +// @Title Get data by ID +// @Description Find rType data based on ID +// @Param ID path string true "The ID of the data resource" +// @Success 200 {object} models.DataModel +// @Failure 403 ID is empty +// @router /:ID [get] +func (o *DataController) GetOneData(ID string) { + if ID != "" { + ob, err := models.GetOneData(ID) + if err != nil { + o.Ctx.Output.SetStatus(500) + } else { + o.Data["json"] = ob + } + } else { + o.Ctx.Output.SetStatus(403) + } + o.ServeJSON() +} + +// @Title Get multiple data by IDs +// @Description Return Data object if found in the DB. Not found IDs will be ignored +// @Param IDs path []string true "List of data IDs" +// @Success 200 {object} []models.DataModel +// @Failure 403 IDs are empty +// @router /multi/:IDs [get] +func (o *DataController) GetMultipleData(IDs []string) { + if len(IDs) != 0 { + ob, err := models.GetMultipleData(IDs) + if err != nil { + o.Ctx.Output.SetStatus(500) + } else { + o.Data["json"] = ob + } + } else { + o.Ctx.Output.SetStatus(403) + } + o.ServeJSON() +} + +// @Title Create Data +// @Description Submit data object +// @Param body body models.DataNEWModel true "The object content" +// @Success 200 {string} ID +// @Failure 403 Missing body or fields +// @router / [post] +func (o *DataController) PostData(body models.DataNEWModel) { + err := validate.Struct(body) + // validationErrors := err.(validator.ValidationErrors) + + if err != nil { + o.Ctx.Output.Status = 403 + o.ServeJSON() + return + } + + ID, err := models.PostOneData(body) + if err != nil { + o.Ctx.Output.SetStatus(500) + return + } + + o.Data["json"] = map[string]string{"ID": ID} + o.ServeJSON() +} diff --git a/controllers/datacenter.go b/controllers/datacenter.go new file mode 100644 index 0000000..0387a32 --- /dev/null +++ b/controllers/datacenter.go @@ -0,0 +1,82 @@ +package controllers + +import ( + "cloud.o-forge.io/core/oc-catalog/models" + + beego "github.com/beego/beego/v2/server/web" + "github.com/go-playground/validator/v10" +) + +// DatacenterController operations about datacenters +type DatacenterController struct { + beego.Controller +} + +func init() { + validate = validator.New() +} + +// @Title Get multiple datacenters by IDs +// @Description Return Datacenter objects if found in the DB. Not found IDs will be ignored +// @Param IDs path []string true "List of datacenter IDs" +// @Success 200 {object} []models.ComputingModel +// @Failure 403 IDs are empty +// @router /multi/:IDs [get] +func (o *DatacenterController) GetMultipleDatacenter(IDs []string) { + if len(IDs) != 0 { + ob, err := models.GetMultipleDatacenter(IDs) + if err != nil { + o.Ctx.Output.SetStatus(500) + } else { + o.Data["json"] = ob + } + } else { + o.Ctx.Output.SetStatus(403) + } + o.ServeJSON() +} + +// @Title GetOneDatacenter +// @Description find datacenter by ID +// @Param ID path string true "the ID you want to get" +// @Success 200 {object} models.DatacenterModel +// @Failure 403 ID is empty +// @router /:ID [get] +func (o *DatacenterController) GetOneDatacenter(ID string) { + if ID != "" { + ob, err := models.GetOneDatacenter(ID) + if err != nil { + o.Data["json"] = err.Error() + } else { + o.Data["json"] = ob + } + } + o.ServeJSON() +} + +// @Title Create Datacenter +// @Description submit Datacenter object +// @Param body body models.DatacenterNEWModel true "The object content" +// @Success 200 {string} models.DatacenterModel +// @Failure 403 Missing body or fields +// @router / [post] +func (o *DatacenterController) PostDatacenter(body models.DatacenterNEWModel) { + err := validate.Struct(body) + // validationErrors := err.(validator.ValidationErrors) + + if err != nil { + o.Data["json"] = err.Error() + o.Ctx.Output.Status = 403 + o.ServeJSON() + return + } + + ID, err := models.PostOneDatacenter(body) + if err != nil { + o.Ctx.Output.SetStatus(500) + return + } + + o.Data["json"] = map[string]string{"ID": ID} + o.ServeJSON() +} diff --git a/controllers/schedule.go b/controllers/schedule.go new file mode 100644 index 0000000..545323c --- /dev/null +++ b/controllers/schedule.go @@ -0,0 +1,163 @@ +package controllers + +import ( + "time" + + "cloud.o-forge.io/core/oc-catalog/models" + "github.com/beego/beego/v2/core/logs" + beego "github.com/beego/beego/v2/server/web" +) + +type ScheduleController struct { + beego.Controller +} + +// @Title Create schedule +// @Description Create schedule for a workflow. It will return some future executions just as information +// @Param dcName query string true "Name of the node (oc-catalog) from where the workflow comes." +// @Param workflowName query string true "Workflow Name" +// @Param cron query string true "Cron syntax with year. If no year is specified, will use the current" +// @Param duration query uint true "Duration in seconds" +// @Param startDate query time.Time true "RFC3339 time for startDate" +// @Param stopDate query time.Time true "RFC3339 time for stopDate" +// @Param requirements body models.ExecutionRequirementsModel true "The object content" +// @Success 200 {object} models.ScheduleInfo +// @Failure 403 Authentication issue +// @Failure 400 workflowName not found or empty +// // @Security jwtAPIToken +// @router /book [post] +func (u *ScheduleController) CreateSchedule(dcName, workflowName, cron string, duration uint, startDate, stopDate time.Time, requirements models.ExecutionRequirementsModel) { + // token := u.Ctx.Input.GetData("jwtAPIToken").(string) + + //FIXME: Passing a date as "2021-07-15 00:00:00 0000 UTC" break the controller but return 200. Should return 4xx + + username := "asd" + + nextIters, err := models.CreateScheduleWorkflow(dcName, username, workflowName, cron, duration, startDate, stopDate, requirements) + if err != nil { + u.CustomAbort(400, err.Error()) + } + + u.Data["json"] = nextIters + u.ServeJSON() + + return +} + +//TODO: This node corresponds to a unique DC, which it owns. We must restructure the code in order to +// allow a unique DC. And maybe discuss more this point + +// @Title Check if schedule can be created in this DC +// @Description Check for availability of this DC +// @Param cron query string true "Cron syntax" +// @Param duration query uint true "Duration in seconds" +// @Param startDate query time.Time true "RFC3339 time for startDate" +// @Param stopDate query time.Time true "RFC3339 time for stopDate" +// @Param requirements body models.ExecutionRequirementsModel true "The object content" +// @Success 200 The schedule can be created +// @Failure 403 Authentication issue +// @Failure 400 Other error. Check the output +// // @Security jwtAPIToken +// @router /check [post] +func (u *ScheduleController) CheckSchedule(cron string, duration uint, startDate, stopDate time.Time, requirements models.ExecutionRequirementsModel) { + // token := u.Ctx.Input.GetData("jwtAPIToken").(string) + + // username := "asd" + + if cron == "" { + u.CustomAbort(400, "Tasks cronString must not be empty") + } + + // Check Dates + if startDate.After(stopDate) || startDate.Equal(stopDate) { + u.CustomAbort(400, "startDate must be before stopDate") + } + + if startDate.Before(time.Now().UTC()) { + u.CustomAbort(400, "Current server time ("+time.Now().UTC().String()+") is after the startDate ("+startDate.String()+")") + } + + err := models.CheckSchedule(cron, duration, startDate, stopDate, requirements) + if err != nil { + logs.Warning(err) + u.CustomAbort(400, err.Error()) + } + + // u.Data["json"] = nextIters + // u.ServeJSON() + + return +} + +// @Title Get schedules +// @Description Get a list of next startDates schedules (inclusive). If timezone is not specified, will assume UTC +// @Param startDate query time.Time true "Start date" +// @Param stopDate query time.Time true "End date" +// @Success 200 {object} []models.ScheduleDB +// @Success 201 Too much elements within the range of dates +// @Failure 403 Authentication issue +// @Failure 400 Other error. Check the output +// // @Security jwtAPIToken +// @router / [get] +func (u *ScheduleController) GetSchedules(startDate time.Time, stopDate time.Time) { + // token := u.Ctx.Input.GetData("jwtAPIToken").(string) + + // username := "asd" + + data, maxLimit, err := models.GetSchedules(startDate, stopDate) + if err != nil { + logs.Warning(err) + u.CustomAbort(400, err.Error()) + } + + if maxLimit { + u.Ctx.Output.Status = 201 + } + + u.Data["json"] = data + u.ServeJSON() + + return +} + +// @Title Get next schedule +// @Description Give a date, get the next date where there are at least on schedule. If no hours specified, will assume 00:00 +// @Param baseDate query time.Time true "Base date" +// @Success 200 {object} *time.Time +// @Failure 403 Authentication issue +// @Failure 400 Other error. Check the output +// // @Security jwtAPIToken +// @router /next [get] +func (u *ScheduleController) GetNextSchedules(baseDate time.Time) { + // token := u.Ctx.Input.GetData("jwtAPIToken").(string) + + // username := "asd" + + futureDate := models.GetFarSchedules(baseDate, true) + + u.Data["json"] = futureDate + u.ServeJSON() + + return +} + +// @Title Get previous schedule +// @Description Give a date, get the previous date where there are at least on schedule. If no hours specified, will assume 00:00 +// @Param baseDate query time.Time true "Base date" +// @Success 200 {object} *time.Time +// @Failure 403 Authentication issue +// @Failure 400 Other error. Check the output +// // @Security jwtAPIToken +// @router /previous [get] +func (u *ScheduleController) GetPreviousSchedules(baseDate time.Time) { + // token := u.Ctx.Input.GetData("jwtAPIToken").(string) + + // username := "asd" + + futureDate := models.GetFarSchedules(baseDate, false) + + u.Data["json"] = futureDate + u.ServeJSON() + + return +} diff --git a/controllers/search.go b/controllers/search.go new file mode 100644 index 0000000..66e54f2 --- /dev/null +++ b/controllers/search.go @@ -0,0 +1,31 @@ +package controllers + +import ( + "cloud.o-forge.io/core/oc-catalog/models" + + beego "github.com/beego/beego/v2/server/web" +) + +type SearchController struct { + beego.Controller +} + +// TODO: Search by word is very very inneficent for not small databases +// @Title Search by word +// @Description find resources by word +// @Param word query string true "Word to search across all resources" +// @Success 200 {object} models.SearchResult +// @Failure 503 Internal error +// @router /byWord [get] +func (o *SearchController) FindByWord(word string) { + if word != "" { + ob, err := models.FindByWord(word) + if err != nil { + o.Data["json"] = err.Error() + o.Ctx.Output.Status = 503 + } else { + o.Data["json"] = ob + } + } + o.ServeJSON() +} diff --git a/controllers/storage.go b/controllers/storage.go new file mode 100644 index 0000000..3caddd1 --- /dev/null +++ b/controllers/storage.go @@ -0,0 +1,77 @@ +package controllers + +import ( + "cloud.o-forge.io/core/oc-catalog/models" + + beego "github.com/beego/beego/v2/server/web" +) + +// StorageController operations about storage +type StorageController struct { + beego.Controller +} + +// @Title Get +// @Description find storage by ID +// @Param ID path string true "the ID you want to get" +// @Success 200 {object} models.StorageModel +// @Failure 403 ID is empty +// @router /:ID [get] +func (o *StorageController) GetOneStorage(ID string) { + if ID != "" { + ob, err := models.GetOneStorage(ID) + if err != nil { + o.Data["json"] = err.Error() + } else { + o.Data["json"] = ob + } + } + o.ServeJSON() +} + +// @Title Get multiple storages by IDs +// @Description Return Storage objects if found in the DB. Not found IDs will be ignored +// @Param IDs path []string true "List of storage IDs" +// @Success 200 {object} []models.ComputingModel +// @Failure 403 IDs are empty +// @router /multi/:IDs [get] +func (o *StorageController) GetMultipleStorage(IDs []string) { + if len(IDs) != 0 { + ob, err := models.GetMultipleStorage(IDs) + if err != nil { + o.Ctx.Output.SetStatus(500) + } else { + o.Data["json"] = ob + } + } else { + o.Ctx.Output.SetStatus(403) + } + o.ServeJSON() +} + +// @Title Create Storage +// @Description submit storage object +// @Param body body models.StorageNEWModel true "The object content" +// @Success 200 {string} models.StorageModel +// @Failure 403 Missing body or fields +// @router / [post] +func (o *StorageController) PostStorage(body models.StorageNEWModel) { + err := validate.Struct(body) + // validationErrors := err.(validator.ValidationErrors) + + if err != nil { + o.Data["json"] = err.Error() + o.Ctx.Output.Status = 403 + o.ServeJSON() + return + } + + ID, err := models.PostOneStorage(body) + if err != nil { + o.Ctx.Output.SetStatus(500) + return + } + + o.Data["json"] = map[string]string{"ID": ID} + o.ServeJSON() +} diff --git a/controllers/tokens.go b/controllers/tokens.go new file mode 100644 index 0000000..414cbb9 --- /dev/null +++ b/controllers/tokens.go @@ -0,0 +1,68 @@ +package controllers + +import ( + "errors" + "time" + + "github.com/beego/beego/v2/core/logs" + "github.com/dgrijalva/jwt-go" +) + +const mySuperSecretKey = "jdnfksdmfksd" + +func CreateToken(userId string) (string, error) { + var err error + //Creating Access Token + // os.Setenv("ACCESS_SECRET", "jdnfksdmfksd") //this should be in an env file + atClaims := jwt.MapClaims{} + atClaims["authorized"] = true + atClaims["user_id"] = userId + atClaims["exp"] = time.Now().UTC().Add(time.Hour * 15).Unix() + at := jwt.NewWithClaims(jwt.SigningMethodHS256, atClaims) + token, err := at.SignedString([]byte(mySuperSecretKey)) + if err != nil { + return "", err + } + return token, nil +} + +func IsValidToken(jwtToken string) (*jwt.Token, error) { + token, err := jwt.Parse(jwtToken, func(token *jwt.Token) (interface{}, error) { + //TODO: Validate expected algorithm + return []byte(mySuperSecretKey), nil + }) + + var message string + + if err == nil && token.Valid { + return token, nil + } else if ve, ok := err.(*jwt.ValidationError); ok { + if ve.Errors&jwt.ValidationErrorMalformed != 0 { + message = "Token " + jwtToken + " is not even a token" + } else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 { + message = "Token is either expired or not active yet" + } else { + message = "Couldn't handle this token: " + err.Error() + } + } + + logs.Debug(message) + return nil, errors.New(message) +} + +func GetUsernameFromToken(token string) (string, error) { + + tokenObj, err := IsValidToken(token) + if err != nil { + logs.Debug(err) + return "", err + } + + if claims, ok := tokenObj.Claims.(jwt.MapClaims); ok { + return claims["user_id"].(string), nil + } + + logs.Debug("Unknow JWT error") + return "", errors.New("Unknow JWT error") + +} diff --git a/controllers/user.go b/controllers/user.go new file mode 100644 index 0000000..9a19d41 --- /dev/null +++ b/controllers/user.go @@ -0,0 +1,54 @@ +package controllers + +import ( + "cloud.o-forge.io/core/oc-catalog/models" + beego "github.com/beego/beego/v2/server/web" +) + +type UserController struct { + beego.Controller +} + +// @Title Login +// @Description Logs user into the system +// @Param username query string true "The username for login" +// @Param password query string true "The password for login" +// @Success 200 {string} login success +// @Failure 403 user not exist +// @router /login [get] +func (u *UserController) Login() { + username := u.GetString("username") + password := u.GetString("password") + + if models.Login(username, password) { + token, err := CreateToken(username) + + if err != nil { + u.Data["json"] = err.Error() + u.Ctx.Output.Status = 503 + u.ServeJSON() + return + } + + u.Ctx.SetCookie("token", token) + u.Ctx.Output.Header("Authorization", token) //FIXME: Some more generic way to use the name of the header + u.Data["json"] = "login success" + u.ServeJSON() + return + + } + u.Ctx.Output.Status = 403 + u.Data["json"] = "user not exist" + u.ServeJSON() + +} + +// @Title logout +// @Description Logs out current logged in user session +// @Success 200 {string} logout success +// // @Security mySecurityPathNameApiKey +// @router /logout [get] +func (u *UserController) Logout() { + u.Data["json"] = "logout success" + u.ServeJSON() +} diff --git a/controllers/workflow.go b/controllers/workflow.go new file mode 100644 index 0000000..db97d61 --- /dev/null +++ b/controllers/workflow.go @@ -0,0 +1,337 @@ +package controllers + +import ( + "time" + + "cloud.o-forge.io/core/oc-catalog/models" + "github.com/beego/beego/v2/core/logs" + beego "github.com/beego/beego/v2/server/web" + "github.com/vk496/cron" +) + +type WorkflowController struct { + beego.Controller +} + +// @Title Create a new workflow +// @Description Create a name for the new workflow +// @Param workflowName query string true "Name of the workflow" +// @Success 200 {string} Workflow created succeful +// @Failure 403 Authentication issue +// @Failure 400 {string} Other error +// // @Security jwtAPIToken +// @router / [post] +func (u *WorkflowController) CreateWorkflow(workflowName string) { + // token := u.Ctx.Input.GetData("jwtAPIToken").(string) + + // //TODO: Implement as swagger security definition (api key?) + // username, err := GetUsernameFromToken(token) + // if err != nil { + // u.Data["json"] = "No valid token" + // u.Ctx.Output.Status = 403 + // u.ServeJSON() + // return + // } + + username := "asd" + + if err := models.CreateWorkflow(username, workflowName); err != nil { + u.Data["json"] = err.Error() + u.Ctx.Output.Status = 400 + u.ServeJSON() + return + } +} + +// @Title List workflows +// @Description List available workflows +// @Success 200 []string List of workflows +// @Failure 403 Authentication issue +// @Failure 400 {string} Other error +// // @Security jwtAPIToken +// @router / [get] +func (u *WorkflowController) ListWorkflows() { + // token := u.Ctx.Input.GetData("jwtAPIToken").(string) + + username := "asd" + + w := models.GetWorkspace(username) + + if w == nil { + // No username + u.Data["json"] = "Workspace doesn't exist" + u.Ctx.Output.Status = 400 + u.ServeJSON() + } + + u.Data["json"] = w.GetWorkflows() + u.ServeJSON() +} + +// @Title Get Workflow +// @Description Get a workflow by name +// @Param workflowName path string true "Workflow Name" +// @Success 200 {object} models.Workflow +// @Failure 403 Authentication issue +// @Failure 400 {string} Other error +// // @Security jwtAPIToken +// @router /:workflowName [get] +func (u *WorkflowController) GetWorkflow(workflowName string) { + // token := u.Ctx.Input.GetData("jwtAPIToken").(string) + + username := "asd" + + proj, err := models.GetWorkflow(username, workflowName) + if err != nil { + u.SetData(err.Error()) + u.Ctx.Output.Status = 400 + u.ServeJSON() + return + } + + u.Data["json"] = proj + u.ServeJSON() +} + +// @Title Add new object to a Workflow +// @Description Create a Rtype object from already added resources to the workspace +// @Param workflowName path string true "workflow Name" +// @Param rID query string true "rID of already existing item in Workspace" +// @Success 200 {string} ID of the new object (rObjID) +// @Failure 403 Authentication issue +// @Failure 400 {string} Other error +// // @Security jwtAPIToken +// @router /:workflowName/add [post] +func (u *WorkflowController) AddElementWorkflow(workflowName, rID string) { + username := "asd" + + rObjID, err := models.CreateObjectInWorkflow(username, workflowName, rID) + if err != nil { + logs.Debug(err.Error()) + u.Data["json"] = err.Error() + u.Ctx.Output.Status = 400 + u.ServeJSON() + return + } + + u.Data["json"] = rObjID + u.ServeJSON() + return +} + +// @Title Parse mxGraph +// @Description If we use this aproach to transofrm mxgraph representation in our representation, we should not use other API calls for modify the project structure or we'll have inconsistencies. +// @Param workflowName path string true "Workflow Name" +// @Param xmlData body string true "Xml representation of the workflow" +// @Success 200 The xmlgraph consumed correctly +// @Success 201 The xmlgraph consumed with issues +// @Failure 403 Authentication issue +// @Failure 400 {string} Other error +// @router /:workflowName/mxGraphParser [post] +func (u *WorkflowController) MxGraphParser(workflowName, xmlData string) { + username := "asd" + err, mxissues := models.ParseMxGraph(username, workflowName, xmlData) + if err != nil { + logs.Debug(err.Error()) + u.CustomAbort(400, err.Error()) + } + + if len(mxissues) > 0 { + strErrors := make([]string, len(mxissues)) + + for i, err := range mxissues { + strErrors[i] = err.Error() + } + + u.Data["json"] = strErrors + u.Ctx.Output.Status = 201 + u.ServeJSON() + return + } + +} + +// @Title Get mxGraph last status +// @Description Obtain the last mxgraph XML status from the workflow +// @Param workflowName path string true "Workflow Name" +// @Success 200 The xmlgraph +// @Success 201 Empty workflow +// @Failure 403 Authentication issue +// @Failure 400 {string} Other error +// @router /:workflowName/mxGraphParser [get] +func (u *WorkflowController) MxGraphParserConsume(workflowName string) { + username := "asd" + xmlData, err := models.GetMxGraph(username, workflowName) + if err != nil { + logs.Debug(err.Error()) + u.Data["json"] = err.Error() + u.Ctx.Output.Status = 400 + u.ServeJSON() + return + } + + if xmlData == nil { + u.Ctx.Output.Status = 201 + } else { + u.Ctx.Output.Status = 200 + u.Ctx.Output.Body([]byte(*xmlData)) + u.ServeXML() + } + return +} + +// @Title Create a realtionship between two Robjects +// @Description Create a Rtype object from already added resources to the workspace +// @Param workflowName path string true "Workflow Name" +// @Param rObjIDsource query string true "Robject source. Usually Data" +// @Param isInput query bool true "If the operation is for input (true) linkage or output (false)" +// @Param rObjIDtarger query string true "Robject where will be written the association" +// @Success 200 {string} ID of the new object (rObjID) +// @Failure 403 Authentication issue +// @Failure 400 {string} Other error +// // @Security jwtAPIToken +// @router /:workflowName/link [post] +func (u *WorkflowController) LinkElementsWorkflow(workflowName, rObjIDsource, rObjIDtarger string, isInput bool) { + username := "asd" + + err := models.LinkObjectsInWorkspace(username, workflowName, rObjIDsource, isInput, rObjIDtarger) + if err != nil { + logs.Debug(err.Error()) + u.Data["json"] = err.Error() + u.Ctx.Output.Status = 400 + u.ServeJSON() + return + } +} + +// @Title Get Schedule +// @Description Obtain the desired schedule of this workflow +// @Param workflowName path string true "Workflow Name" +// @Success 200 {object} models.ScheduleTime +// @Failure 403 Authentication issue +// @Failure 400 Workflow doesn't exist +// @Failure 401 Other error +// @router /:workflowName/schedule [get] +func (u *WorkflowController) GetWorkflowSchedule(workflowName string) { + username := "asd" + sched, err := models.GetWorkflowSchedule(username, workflowName) + + // Some error + if err != nil { + u.CustomAbort(401, err.Error()) + } + + // No workflow + if sched == nil { + u.Ctx.Output.Status = 400 + return + } + + u.Ctx.Output.Status = 200 + u.Data["json"] = sched + u.ServeJSON() +} + +// @Title Set Schedule +// @Description Set desired schedule by the user. No other effects a part of saving the user input +// @Param workflowName path string true "Workflow Name" +// @Param isService query bool true "True: Service, False: Task" +// @Param startDate query time.Time true "RFC3339 time for startDate" +// @Param stopDate query time.Time true "RFC3339 time for stopDate" +// @Param events query string false "List of events separated by comma" +// @Param cronString query string false "Cron string" +// @Param duration query uint false "Duration in seconds" +// @Success 200 {object} models.ScheduleInfo +// @Failure 403 Authentication issue +// @Failure 400 Workflow doesn't exist +// @Failure 401 Other error +// @Failure 402 Bad user input +// @router /:workflowName/schedule [put] +func (u *WorkflowController) SetWorkflowSchedule(workflowName, cronString, events string, isService bool, startDate, stopDate time.Time, duration uint) { + username := "asd" + + // Check Dates + if startDate.After(stopDate) || startDate.Equal(stopDate) { + u.CustomAbort(402, "startDate must be before stopDate") + } + + if startDate.Before(time.Now().UTC()) { + u.CustomAbort(402, "Current server time ("+time.Now().UTC().String()+") is after the startDate ("+startDate.String()+")") + } + + // Tasks must have cron and duration + if !isService { + if cronString == "" { + u.CustomAbort(402, "Tasks cronString must not be empty") + } + + if duration == 0 { + u.CustomAbort(402, "Tasks duration musn't be 0") + } + + _, err := cron.Parse(cronString) + // Check cron + if err != nil { + u.CustomAbort(402, "Bad cron message: "+err.Error()) + } + + } + + schedInfo, err := models.SetWorkflowSchedule(username, workflowName, cronString, events, isService, startDate, stopDate, duration) + + // Some error + if err != nil { + u.CustomAbort(401, err.Error()) + } + + // No workflow + if schedInfo == nil { + u.CustomAbort(400, "") + } + + u.Ctx.Output.Status = 200 + u.Data["json"] = schedInfo + u.ServeJSON() +} + +// @Title Check Schedule +// @Description Check if we can schedule the project in other DCs. Must set a desired schedule first! +// @Param workflowName path string true "Workflow Name" +// @Success 200 {object} []models.DCstatus +// @Failure 403 Authentication issue +// @Failure 401 Other error +// @router /:workflowName/schedule/check [get] +func (u *WorkflowController) CheckWorkflowSchedule(workflowName string) { + username := "asd" + + data, err := models.CheckAndBookWorkflowSchedule(username, workflowName, false) + if err != nil { + u.CustomAbort(401, err.Error()) + } + + u.Ctx.Output.Status = 200 + u.Data["json"] = data + u.ServeJSON() + return +} + +// @Title Book Schedule +// @Description Book a schedule in all DCs of the workflow. Must set a desired schedule first! +// @Param workflowName path string true "Workflow Name" +// @Success 200 {object} []models.DCstatus +// @Failure 403 Authentication issue +// @Failure 401 Other error. Check output +// @router /:workflowName/schedule/book [post] +func (u *WorkflowController) BookWorkflowSchedule(workflowName string) { + username := "asd" + + data, err := models.CheckAndBookWorkflowSchedule(username, workflowName, true) + if err != nil { + u.CustomAbort(401, err.Error()) + } + + u.Ctx.Output.Status = 200 + u.Data["json"] = data + u.ServeJSON() + return +} diff --git a/controllers/workspace.go b/controllers/workspace.go new file mode 100644 index 0000000..9c69bed --- /dev/null +++ b/controllers/workspace.go @@ -0,0 +1,151 @@ +package controllers + +import ( + "cloud.o-forge.io/core/oc-catalog/models" + beego "github.com/beego/beego/v2/server/web" +) + +type WorkspaceController struct { + beego.Controller +} + +// @Title Add model to workspace +// @Description Insert a resource in the workspace +// @Param id query string true "ID of a resource" +// @Param rtype query string true "Type of resource" +// @Success 200 {string} login success +// @Failure 403 Authentication issue +// @Failure 400 {string} Other error +// // @Security jwtAPIToken +// @router / [post] +func (u *WorkspaceController) AddModel(id, rtype string) { + // token := u.Ctx.Input.GetData("jwtAPIToken").(string) + + // //TODO: Implement as swagger security definition (api key?) + // username, err := GetUsernameFromToken(token) + // if err != nil { + // u.Data["json"] = "No valid token" + // u.Ctx.Output.Status = 403 + // u.ServeJSON() + // return + // } + + var err error + username := "asd" + + w := models.GetWorkspace(username) + + if w == nil { + w, err = models.NewWorkspace(username) + if err != nil { + u.Data["json"] = err.Error() + u.Ctx.Output.Status = 400 + u.ServeJSON() + return + } + } + + // w.NewResource(id, rtype) + + if err := models.AddResource(username, id, rtype); err != nil { + u.Data["json"] = err.Error() + u.Ctx.Output.Status = 400 + u.ServeJSON() + return + } +} + +// @Title Get workspace +// @Description Get workspace elements based on user_id token +// @Success 200 {object} models.Workspace +// @Failure 403 Authentication issue +// // @Security jwtAPIToken +// @router /list [get] +func (u *WorkspaceController) ListWorkspace() { + // token := u.Ctx.Input.GetData("jwtAPIToken").(string) + + // //TODO: Implement as swagger security definition (api key?) + // username, err := GetUsernameFromToken(token) + // if err != nil { + // u.Data["json"] = "No valid token" + // u.Ctx.Output.Status = 403 + // u.ServeJSON() + // return + // } + + username := "asd" + + ws := models.GetWorkspace(username) + + // if ws == nil { + // u.Ctx.Output.Status = 503 + // return + // } + + u.Data["json"] = ws + u.ServeJSON() + +} + +// @Title Get full workspace +// @Description Get full workspace elements based on user_id token +// @Success 200 {object} models.WorkspaceModel +// @Failure 403 Authentication issue +// // @Security jwtAPIToken +// @router /list_model [get] +func (u *WorkspaceController) ListWorkspaceModel() { + // token := u.Ctx.Input.GetData("jwtAPIToken").(string) + + // //TODO: Implement as swagger security definition (api key?) + // username, err := GetUsernameFromToken(token) + // if err != nil { + // u.Data["json"] = "No valid token" + // u.Ctx.Output.Status = 403 + // u.ServeJSON() + // return + // } + + username := "asd" + + val, err := models.ListFullWorkspace(username) + + if err != nil { + u.Ctx.Output.Status = 503 + return + } + + u.Data["json"] = val + u.ServeJSON() + +} + +// @Title Delete element from user workspace +// @Description Remove a resource from the workspace +// @Param id query string true "ID of a resource" +// @Param rtype query string true "Type of resource" +// @Success 200 {string} Removed succeful +// @Failure 403 Authentication issue +// @Failure 400 {string} Other error +// // @Security jwtAPIToken +// @router / [delete] +func (u *WorkspaceController) DeleteElement(id, rtype string) { + // token := u.Ctx.Input.GetData("jwtAPIToken").(string) + + // //TODO: Implement as swagger security definition (api key?) + // username, err := GetUsernameFromToken(token) + // if err != nil { + // u.Data["json"] = "No valid token" + // u.Ctx.Output.Status = 403 + // u.ServeJSON() + // return + // } + + username := "asd" + + if err := models.RemoveResource(username, id, rtype); err != nil { + u.Data["json"] = err.Error() + u.Ctx.Output.Status = 400 + u.ServeJSON() + return + } +} diff --git a/docker-compose.backend.yml b/docker-compose.backend.yml new file mode 100644 index 0000000..0ea62ff --- /dev/null +++ b/docker-compose.backend.yml @@ -0,0 +1,15 @@ +version: '3.4' + +services: + oc-catalog: + build: . + container_name: oc-catalog + restart: always + environment: + - DOCKER_DCNAME=DC_myDC + depends_on: + - mongo + networks: + - catalog + ports: + - 49618:49618 \ No newline at end of file diff --git a/docker-compose.multi.yml b/docker-compose.multi.yml new file mode 100644 index 0000000..4ae8d6e --- /dev/null +++ b/docker-compose.multi.yml @@ -0,0 +1,27 @@ +version: '3.4' + +services: + dc1: + build: . + restart: always + container_name: dc1 + environment: + - DOCKER_DCNAME=DC_superDC1 + depends_on: + - mongo + networks: + - catalog + ports: + - 49619:49618 + dc2: + build: . + restart: always + container_name: dc2 + environment: + - DOCKER_DCNAME=DC_superDC2 + depends_on: + - mongo + networks: + - catalog + ports: + - 49620:49618 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..32644f9 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,30 @@ +version: '3.4' + +services: + mongo: + image: 'mongo:latest' + networks: + - catalog + ports: + - 27017:27017 + container_name: mongo + volumes: + - oc-catalog-data:/data/db + - oc-catalog-data:/data/configdb + + mongo-express: + image: "mongo-express:latest" + restart: always + depends_on: + - mongo + networks: + - catalog + ports: + - 8081:8081 + +volumes: + oc-catalog-data: + +networks: + catalog: + name: catalog \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..4db8a89 --- /dev/null +++ b/go.mod @@ -0,0 +1,32 @@ +module cloud.o-forge.io/core/oc-catalog + +go 1.15 + +require github.com/beego/beego/v2 v2.0.1 + +require ( + github.com/antihax/optional v1.0.0 + github.com/aws/aws-sdk-go v1.36.29 // indirect + github.com/beego/bee/v2 v2.0.2 + github.com/dgrijalva/jwt-go v3.2.0+incompatible + github.com/go-playground/validator/v10 v10.4.1 + github.com/golang/snappy v0.0.2 // indirect + github.com/klauspost/compress v1.11.7 // indirect + github.com/leodido/go-urn v1.2.1 // indirect + github.com/mitchellh/mapstructure v1.4.1 // indirect + github.com/prometheus/client_golang v1.9.0 // indirect + github.com/prometheus/procfs v0.3.0 // indirect + github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect + github.com/vk496/cron v1.2.0 + go.mongodb.org/mongo-driver v1.4.5 + golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect + golang.org/x/mod v0.4.1 // indirect + golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect + golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 + golang.org/x/sync v0.0.0-20201207232520-09787c993a3a // indirect + golang.org/x/text v0.3.5 // indirect + golang.org/x/tools v0.1.0 // indirect + google.golang.org/protobuf v1.25.0 // indirect + gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b405084 --- /dev/null +++ b/go.sum @@ -0,0 +1,747 @@ +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= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +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/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +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/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= +github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk= +github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= +github.com/aws/aws-sdk-go v1.36.29 h1:lM1G3AF1+7vzFm0n7hfH8r2+750BTo+6Lo6FtPB7kzk= +github.com/aws/aws-sdk-go v1.36.29/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/beego/bee/v2 v2.0.2 h1:xWARyIqdnnbNMDBDUdb6Gvr9S/yGXC6Ni43kKdS1/eg= +github.com/beego/bee/v2 v2.0.2/go.mod h1:rfZa899qLAF8SYBRvE7mWNPZTU7/qysOBhaCLmZrMX4= +github.com/beego/beego/v2 v2.0.1 h1:07a7Z0Ok5vbqyqh+q53sDPl9LdhKh0ZDy3gbyGrhFnE= +github.com/beego/beego/v2 v2.0.1/go.mod h1:8zyHi1FnWO1mZLwTn62aKRIZF/aIKvkCBB2JYs+eqQI= +github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ= +github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU= +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 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +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/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60= +github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.25+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +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-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 v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +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/cosiner/argv v0.1.0/go.mod h1:EusR6TucWKX+zFgtdUsKT2Cvg45K5rtpCcWz4hK06d8= +github.com/couchbase/go-couchbase v0.0.0-20200519150804-63f3cdb75e0d/go.mod h1:TWI8EKQMs5u5jLKW/tsb9VwauIrMIxQG1r5fMsswK5U= +github.com/couchbase/gomemcached v0.0.0-20200526233749-ec430f949808/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c= +github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/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/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY= +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/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +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/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/elastic/go-elasticsearch/v6 v6.8.5/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI= +github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk= +github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA= +github.com/flosch/pongo2 v0.0.0-20200529170236-5abacdfa4915/go.mod h1:fB4mx6dzqFinCxIf3a7Mf5yLk+18Bia9mPAnuejcvDA= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c/go.mod h1:Gja1A+xZ9BoviGJNA2E9vFkPjjsl+CoJxSXiQM1UXtw= +github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= +github.com/go-delve/delve v1.5.0/go.mod h1:c6b3a1Gry6x8a4LGCe/CWzrocrfaHvkUxCj3k4bvSUQ= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +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-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +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-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +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/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= +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= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +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/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= +github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +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= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-dap v0.2.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +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/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/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +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/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +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/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +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.8/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/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= +github.com/juju/errors v0.0.0-20190930114154-d42613fe1ab9/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= +github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= +github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +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/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.11.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg= +github.com/klauspost/compress v1.11.7/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/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +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/ledisdb/ledisdb v0.0.0-20200510135210-d35789ec47e6/go.mod h1:n931TsDuKuq+uX4v1fulaMbA/7ZLLhjc85h7chZGBCQ= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +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-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +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/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +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 v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.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.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +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/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= +github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= +github.com/peterh/liner v1.0.1-0.20171122030339-3681c2a91233/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/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/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +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.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.7.0/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.9.0 h1:Rrch9mh17XcxvEu9D9DEpb4isxjGBtcevQjKvxPRQIU= +github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/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.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +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.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.15.0 h1:4fgOnadei3EZvgRwxJ7RMpG1k1pOZth5Pc13tyspaKM= +github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/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.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +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.3.0 h1:Uehi/mxLK0eiUc0H0++5tpMGTexB8wZ598MIgU8VpDM= +github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg= +github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 h1:DAYUYH5869yV94zvCES9F51oYtN5oGlwjxJJz7ZCnik= +github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw= +github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400/go.mod h1:DDcKzU3qCuvj/tPnimWSsZZzvk9qvkvrIL5naVBPh5s= +github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.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/smartwalle/pongo2render v1.0.1/go.mod h1:MGnTzND7nEMz7g194kjlnw8lx/V5JJlb1hr5kDXEO0I= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +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/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.0-20170417170307-b6cb39589372/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v0.0.0-20170417173400-9e4c21054fa1/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/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= +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/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.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= +github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= +github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +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 v0.0.0-20171122102828-84cb69a8af83/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= +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/vk496/cron v1.2.0 h1:fDxb4qNi6Rmxh3h9snW1sKJ0nHgjpg3fYc0Oq+igbvk= +github.com/vk496/cron v1.2.0/go.mod h1:f8lpm+SIXbjvujp8Dix4S2B+GGva/q0yrRPQ8hwTtOc= +github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc h1:n+nNi93yXLkJvKwXNP9d55HC7lGK4H/SRcwB5IaUZLo= +github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU= +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/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.etcd.io/etcd v3.3.25+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI= +go.mongodb.org/mongo-driver v1.4.5 h1:TLtO+iD8krabXxvY1F1qpBOHgOxhLWR7XsT7kQeRmMY= +go.mongodb.org/mongo-driver v1.4.5/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +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= +go.starlark.net v0.0.0-20190702223751-32f345186213/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg= +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/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= +golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/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/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +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= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/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= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.3.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/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-20181023162649-9b4f9f5ad519/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-20181201002055-351d144fa1fc/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-20190125091013-d26f9f9a57f3/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-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-20190620200207-3b0461eec859/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-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/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-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +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 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +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= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/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 h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +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-20181026203630-95b1ffbd15a5/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-20181122145206-62eef0e2fa9b/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-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/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-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +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.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +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/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-20180828015842-6cd1fcedba52/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-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +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-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/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-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/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-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= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +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.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +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= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +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= +google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +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-20190530194941-fb225487d101/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= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= +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.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +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.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +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= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +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= +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-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-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +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/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +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.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 h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +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= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/main.go b/main.go new file mode 100644 index 0000000..49e14ed --- /dev/null +++ b/main.go @@ -0,0 +1,31 @@ +package main + +import ( + "os" + + "cloud.o-forge.io/core/oc-catalog/routers" + "cloud.o-forge.io/core/oc-catalog/services" + + beego "github.com/beego/beego/v2/server/web" +) + +func main() { + + // If we have any parameter, we run the beego directly + if len(os.Args) > 1 { + beego.Run() + } + + routers.Init() + services.Init() + + if beego.BConfig.RunMode == "dev" { + // beego.BConfig.WebConfig.DirectoryIndex = true + beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger" + } + beego.Run() + + defer func() { + services.MongoDisconnect() + }() +} diff --git a/models/computing.go b/models/computing.go new file mode 100644 index 0000000..5169077 --- /dev/null +++ b/models/computing.go @@ -0,0 +1,164 @@ +package models + +import ( + "cloud.o-forge.io/core/oc-catalog/models/rtype" + "cloud.o-forge.io/core/oc-catalog/services" + "github.com/beego/beego/v2/core/logs" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +type ExecutionRequirementsModel struct { + CPUs uint `json:"cpus" required:"true"` + GPUs uint `json:"gpus" description:"Amount of GPUs needed"` + RAM uint `json:"ram" required:"true" description:"Units in MB"` + + // We should check closely how to deal with storage, since they are independent models + // but also part of a DataCenter + // Storage uint `json:"storage" description:"Units in MB"` + + Parallel bool `json:"parallel"` + ScalingModel uint `json:"scaling_model"` + DiskIO string `json:"disk_io"` +} + +type RepositoryModel struct { + Credentials string `json:"credentials"` + Url string `json:"url"` +} + +type ComputingNEWModel struct { + Description string `json:"description" required:"true"` + Name string `json:"name,omitempty" required:"true" validate:"required" description:"Name of the computing"` + ShortDescription string `json:"short_description" required:"true" validate:"required"` + Logo string `json:"logo" required:"true" validate:"required"` + + Type string `json:"type,omitempty" required:"true"` + Owner string `json:"owner"` + License string `json:"license"` + Price uint `json:"price"` + + ExecutionRequirements ExecutionRequirementsModel `json:"execution_requirements"` + + Dinputs []string + Doutputs []string + + Repository RepositoryModel `json:"repository"` +} + +type ComputingModel struct { + ID string `json:"ID" bson:"_id" required:"true" example:"5099803df3f4948bd2f98391"` + ComputingNEWModel `bson:",inline"` +} + +func (model ComputingModel) getRtype() rtype.Rtype { + return rtype.COMPUTING +} + +func (model ComputingModel) getName() string { + return model.Name +} + +// A user can have multiple workload project with the same model. We must distinguish what is +// the model and what is the user object + +type ComputingObject struct { + ReferenceID primitive.ObjectID `json:"referenceID" description:"Computing model ID"` + + Inputs []string `json:"inputs"` + Outputs []string `json:"outputs"` + + DataCenterID string `json:"datacenterID" description:"Datacenter where the computing will be executed"` +} + +func (obj ComputingObject) getHost() *string { + return nil // Host is DC only attribute +} + +func (obj *ComputingObject) setReference(rID primitive.ObjectID) { + obj.ReferenceID = rID +} + +func (obj ComputingObject) getReference() primitive.ObjectID { + return obj.ReferenceID +} + +func (obj ComputingObject) getRtype() rtype.Rtype { + return rtype.COMPUTING +} + +func (obj ComputingObject) getModel() (ret ResourceModel, err error) { + var ret2 ComputingModel + res := services.MngoCollComputing.FindOne(services.MngoCtx, + primitive.M{"_id": obj.ReferenceID}, + ) + + if err = res.Err(); err != nil { + return + } + + err = res.Decode(&ret2) + return ret2, err +} + +func (obj ComputingObject) getName() (name *string) { + + aa, err := obj.getModel() + + if err != nil { + logs.Warn(err) + return + } + + name2 := aa.getName() + + return &name2 +} + +func (obj ComputingObject) isLinked(rObjID string) LinkingState { + if contains(obj.Inputs, rObjID) { + return INPUT + } + + if contains(obj.Outputs, rObjID) { + return OUTPUT + } + + return NO_LINK +} + +func (obj *ComputingObject) addLink(direction LinkingState, rID string) { + switch direction { + case INPUT: + obj.Inputs = append(obj.Inputs, rID) + case OUTPUT: + obj.Outputs = append(obj.Outputs, rID) + } +} + +func GetOneComputing(ID string) (object *ComputingModel, err error) { + obj, err := getOneResourceByID(ID, rtype.COMPUTING) + + if err != nil { + return object, err + } + + object = obj.(*ComputingModel) + + return object, err +} + +func GetMultipleComputing(IDs []string) (object *[]ComputingModel, err error) { + objArray, err := getMultipleResourceByIDs(IDs, rtype.COMPUTING) + + if err != nil { + return nil, err + } + + object = objArray.(*[]ComputingModel) + + return object, err +} + +func PostOneComputing(obj ComputingNEWModel) (ID string, err error) { + return postOneResource(obj, rtype.COMPUTING) +} diff --git a/models/data.go b/models/data.go new file mode 100644 index 0000000..eb36d25 --- /dev/null +++ b/models/data.go @@ -0,0 +1,125 @@ +package models + +import ( + "cloud.o-forge.io/core/oc-catalog/models/rtype" + "cloud.o-forge.io/core/oc-catalog/services" + "github.com/beego/beego/v2/core/logs" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +// TODO: Define better the different types of Data model with model herarchy +// TODO: review why swagger are not using the metadata when we do herarchy +type DataNEWModel struct { + Name string `json:"name,omitempty" required:"true" validate:"required" description:"Name of the data"` + ShortDescription string `json:"short_description" required:"true" validate:"required"` + Logo string `json:"logo" required:"true" validate:"required"` + Description string `json:"description" required:"true" validate:"required"` + + Type string `json:"type,omitempty" required:"true" validate:"required" description:"Define type of data" example:"file"` + Example string `json:"example" required:"true" validate:"required" description:"base64 encoded data"` + Location string `json:"location" required:"true" validate:"required"` + Dtype string `json:"dtype"` + Protocol []string `json:"protocol"` //TODO Enum type +} + +type DataModel struct { + ID string `json:"ID" bson:"_id" required:"true" validate:"required"` + DataNEWModel `bson:",inline"` +} + +func (obj DataModel) getRtype() rtype.Rtype { + return rtype.DATA +} + +func (model DataModel) getName() string { + return model.Name +} + +type DataIO struct { + Counter uint `description:"Incremental number starting from 0"` +} + +type DataObject struct { + ReferenceID primitive.ObjectID `json:"referenceID" description:"Data model ID"` +} + +func (obj DataObject) getHost() *string { + return nil // Host is DC only attribute +} + +func (obj DataObject) getModel() (ret ResourceModel, err error) { + var ret2 DataModel + res := services.MngoCollData.FindOne(services.MngoCtx, + primitive.M{"_id": obj.ReferenceID}, + ) + + if err = res.Err(); err != nil { + return + } + + err = res.Decode(&ret2) + return ret2, err +} + +func (obj *DataObject) setReference(rID primitive.ObjectID) { + obj.ReferenceID = rID +} + +func (obj DataObject) getReference() primitive.ObjectID { + return obj.ReferenceID +} + +func (obj DataObject) getRtype() rtype.Rtype { + return rtype.DATA +} + +func (obj DataObject) getName() (name *string) { + + res := services.MngoCollData.FindOne(services.MngoCtx, primitive.M{"_id": obj.ReferenceID}) + + if res.Err() != nil { + logs.Error(res) + return + } + + var ret DataModel + res.Decode(&ret) + + return &ret.Name +} + +func (obj DataObject) isLinked(rID string) LinkingState { + return NO_LINK +} + +func (obj *DataObject) addLink(direction LinkingState, rObjID string) { + +} + +func PostOneData(obj DataNEWModel) (string, error) { + return postOneResource(obj, rtype.DATA) +} + +func GetMultipleData(IDs []string) (object *[]DataModel, err error) { + objArray, err := getMultipleResourceByIDs(IDs, rtype.DATA) + + if err != nil { + return nil, err + } + + object = objArray.(*[]DataModel) + + return object, err +} + +func GetOneData(ID string) (object *DataModel, err error) { + obj, err := getOneResourceByID(ID, rtype.DATA) + + if err != nil { + return nil, err + } + + object = obj.(*DataModel) + + return object, err +} diff --git a/models/datacenter.go b/models/datacenter.go new file mode 100644 index 0000000..3a66515 --- /dev/null +++ b/models/datacenter.go @@ -0,0 +1,212 @@ +package models + +import ( + "net" + "time" + + "cloud.o-forge.io/core/oc-catalog/models/rtype" + "cloud.o-forge.io/core/oc-catalog/services" + "github.com/beego/beego/v2/core/logs" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +type DatacenterCpuModel struct { + Cores uint `json:"cores" required:"true"` //TODO: validate + Architecture string `json:"architecture"` //TOOD: enum + Shared bool `json:"shared"` + MinimumMemory uint `json:"minimum_memory"` + Platform string `json:"platform"` +} + +type DatacenterMemoryModel struct { + Size uint `json:"size" description:"Units in MB"` + Ecc bool `json:"ecc"` +} + +type DatacenterGpuModel struct { + CudaCores uint `json:"cuda_cores"` + Model string `json:"model"` + Memory uint `json:"memory" description:"Units in MB"` + TensorCores uint `json:"tensor_cores"` +} + +type DatacenterNEWModel struct { + Name string `json:"name" required:"true"` + Type string `json:"type,omitempty" required:"true"` + Acronym string `json:"acronym" required:"true" description:"id of the DC"` + Hosts []string `json:"hosts" required:"true" description:"list of host:port"` + Description string `json:"description" required:"true"` + ShortDescription string `json:"short_description" required:"true" validate:"required"` + Logo string `json:"logo" required:"true" validate:"required"` + + CPU DatacenterCpuModel `json:"cpu" required:"true"` + RAM DatacenterMemoryModel `json:"ram" required:"true"` + GPU []DatacenterGpuModel `json:"gpu" required:"true"` + + Owner string `json:"owner" ` + BookingPrice int `json:"bookingPrice" ` +} + +type DatacenterModel struct { + ID string `json:"ID" bson:"_id" required:"true"` + DatacenterNEWModel `bson:",inline"` +} + +func GetDatacenterFromAcronym(DC_name string) (retObj *DatacenterModel) { + // TODO: This call should get the data from the peers, since it could be a different + // host in the future + res := services.MngoCollDatacenter.FindOne(services.MngoCtx, primitive.M{"acronym": DC_name}) + + if res.Err() != nil { + logs.Error(res) + return + } + + var ret DatacenterModel + res.Decode(&ret) + + return &ret +} + +func (obj DatacenterModel) GetTotalCPUs() uint { + return obj.CPU.Cores +} + +func (obj DatacenterModel) GetTotalGPUs() uint { + return uint(len(obj.GPU)) +} + +func (obj DatacenterModel) GetTotalRAM() uint { + return obj.RAM.Size +} + +func (obj DatacenterModel) getRtype() rtype.Rtype { + return rtype.DATACENTER +} + +func (model DatacenterModel) getName() string { + return model.Name +} + +type DatacenterObject struct { + ReferenceID primitive.ObjectID `json:"referenceID" description:"Data model ID"` +} + +func (obj *DatacenterObject) setReference(rID primitive.ObjectID) { + obj.ReferenceID = rID +} + +func (obj DatacenterObject) getModel() (ret ResourceModel, err error) { + var ret2 DatacenterModel + res := services.MngoCollDatacenter.FindOne(services.MngoCtx, + primitive.M{"_id": obj.ReferenceID}, + ) + + if err = res.Err(); err != nil { + return + } + + err = res.Decode(&ret2) + return ret2, err +} + +func (obj DatacenterObject) getReference() primitive.ObjectID { + return obj.ReferenceID +} + +// Return a reachable host. If no one is reachable, return the first entry +func (obj DatacenterObject) getHost() (host *string) { + res := services.MngoCollDatacenter.FindOne(services.MngoCtx, primitive.M{"_id": obj.ReferenceID}) + + if res.Err() != nil { + logs.Error(res) + return nil + } + + var ret DatacenterModel + + err := res.Decode(&ret) + if err != nil { + logs.Error(res) + return nil + } + + host = GetHost(ret.Hosts) + return +} + +func GetHost(hosts []string) (host *string) { + // Return the first one if we can't reach any server + host = &hosts[0] + + for _, singleHost := range hosts { + conn, err := net.DialTimeout("tcp", singleHost, time.Duration(3)*time.Second) //FIXME: longer wait for connection in the future? + + if err != nil { + continue + } + + if conn != nil { + //bingo + host = &singleHost + conn.Close() + return + } + } + return +} + +func (obj DatacenterObject) getRtype() rtype.Rtype { + return rtype.DATACENTER +} + +func (obj DatacenterObject) getName() (name *string) { + + res := services.MngoCollDatacenter.FindOne(services.MngoCtx, primitive.M{"_id": obj.ReferenceID}) + + if res.Err() != nil { + logs.Error(res) + return + } + + var ret DatacenterModel + res.Decode(&ret) + + return &ret.Name +} + +func (obj DatacenterObject) isLinked(rID string) LinkingState { + return NO_LINK +} + +func (obj *DatacenterObject) addLink(direction LinkingState, rObjID string) { + +} + +func PostOneDatacenter(obj DatacenterNEWModel) (string, error) { + return postOneResource(obj, rtype.DATACENTER) +} + +func GetMultipleDatacenter(IDs []string) (object *[]DatacenterModel, err error) { + objArray, err := getMultipleResourceByIDs(IDs, rtype.DATACENTER) + + if err != nil { + return nil, err + } + + object = objArray.(*[]DatacenterModel) + + return object, err +} + +func GetOneDatacenter(ID string) (object *DatacenterModel, err error) { + obj, err := getOneResourceByID(ID, rtype.DATACENTER) + + if err != nil { + return object, err + } + + object = obj.(*DatacenterModel) //TODO: fix a possible segfault in this model and the others + + return object, err +} diff --git a/models/generic.go b/models/generic.go new file mode 100644 index 0000000..33088d7 --- /dev/null +++ b/models/generic.go @@ -0,0 +1,118 @@ +package models + +import ( + "errors" + + "cloud.o-forge.io/core/oc-catalog/models/rtype" + "cloud.o-forge.io/core/oc-catalog/services" + "github.com/beego/beego/v2/core/logs" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo/options" +) + +func getObjIDFromString(id string) interface{} { + objectID, err := primitive.ObjectIDFromHex(id) + if err == nil { + return objectID + } + + return id +} + +func getMultipleObjIDFromArray(ids []string) []interface{} { + + var ret []interface{} + + for _, val := range ids { + ret = append(ret, getObjIDFromString(val)) + } + + return ret + +} + +func getOneResourceByID(ID string, rType rtype.Rtype) (obj interface{}, err error) { + + targetDBCollection := rType.MongoCollection() + var retObj interface{} + + // asd := rType. + switch rType { + case rtype.DATA: + retObj = &DataModel{} + case rtype.COMPUTING: + retObj = &ComputingModel{} + case rtype.STORAGE: + retObj = &StorageModel{} + case rtype.DATACENTER: + retObj = &DatacenterModel{} + default: + message := "Rtype " + rType.String() + " is not implemented" + logs.Error(message) + return nil, errors.New(message) + } + + filter := bson.M{"_id": getObjIDFromString(ID)} + + res := targetDBCollection.FindOne(services.MngoCtx, filter) + res.Decode(retObj) + + if res.Err() != nil { + logs.Warn("Couldn't find resource: " + res.Err().Error()) + } + + return retObj, res.Err() +} + +func getMultipleResourceByIDs(IDs []string, rType rtype.Rtype) (interface{}, error) { + + targetDBCollection := rType.MongoCollection() + var retObj interface{} + + // asd := rType. + switch rType { + case rtype.DATA: + retObj = &[]DataModel{} + case rtype.COMPUTING: + retObj = &[]ComputingModel{} + case rtype.STORAGE: + retObj = &[]StorageModel{} + case rtype.DATACENTER: + retObj = &[]DatacenterModel{} + default: + message := "Rtype " + rType.String() + " is not implemented" + logs.Error(message) + return nil, errors.New(message) + } + + filter := bson.M{"_id": bson.M{"$in": getMultipleObjIDFromArray(IDs)}} + + //FIXME: Limit of find + res, err := targetDBCollection.Find(services.MngoCtx, + filter, + options.Find().SetLimit(100), + ) + + if err != nil { + logs.Warn("Couldn't find multiple data: " + err.Error()) + return nil, err + } + res.All(services.MngoCtx, retObj) + + return retObj, res.Err() +} + +func postOneResource(retObj interface{}, rType rtype.Rtype) (ID string, err error) { + + targetDBCollection := rType.MongoCollection() + + result, err := targetDBCollection.InsertOne(services.MngoCtx, retObj) + if err != nil { + logs.Warn("Couldn't insert resource: " + err.Error()) + return "", err + } + + return result.InsertedID.(primitive.ObjectID).Hex(), nil + +} diff --git a/models/mxgraph.go b/models/mxgraph.go new file mode 100644 index 0000000..7f227fc --- /dev/null +++ b/models/mxgraph.go @@ -0,0 +1,35 @@ +package models + +import ( + "encoding/xml" +) + +type MxGraphModel struct { + XMLName xml.Name `xml:"mxGraphModel"` + + Root struct { + XMLName xml.Name `xml:"root"` + MxCell []MxCell `xml:"mxCell"` + } +} + +type MxCell struct { + XMLName xml.Name `xml:"mxCell"` + ID string `xml:"id,attr"` + Parent *string `xml:"parent,attr"` + RID *string `xml:"rID,attr"` + Source *string `xml:"source,attr"` + Target *string `xml:"target,attr"` +} + +type mxissue struct { + msg string +} + +func (m *mxissue) Error() string { + return m.msg +} + +func newMxIssue(message string) error { + return &mxissue{message} +} diff --git a/models/rtype/rtype.go b/models/rtype/rtype.go new file mode 100644 index 0000000..bd934ae --- /dev/null +++ b/models/rtype/rtype.go @@ -0,0 +1,73 @@ +package rtype + +import ( + "cloud.o-forge.io/core/oc-catalog/services" + "github.com/beego/beego/v2/core/logs" + "go.mongodb.org/mongo-driver/mongo" +) + +//http://www.inanzzz.com/index.php/post/wqbs/a-basic-usage-of-int-and-string-enum-types-in-golang + +type Rtype int + +const ( + INVALID Rtype = iota + DATA + COMPUTING + STORAGE + DATACENTER +) + +var extensions = [...]string{ + "INVALID", + "data", + "computing", + "storage", + "datacenter", +} + +func IsValidRtype(input string) bool { + for _, v := range extensions { + if v == input { + return true + } + } + + return false +} + +func NewRtype(rType string) Rtype { + switch rType { + case DATA.String(): + return DATA + case COMPUTING.String(): + return COMPUTING + case STORAGE.String(): + return STORAGE + case DATACENTER.String(): + return DATACENTER + default: + return INVALID + } +} + +func (e Rtype) String() string { + return extensions[e] +} + +func (e Rtype) MongoCollection() *mongo.Collection { + switch e { + case DATA: + return services.MngoCollData + case COMPUTING: + return services.MngoCollComputing + case STORAGE: + return services.MngoCollStorage + case DATACENTER: + return services.MngoCollDatacenter + default: + message := "Rtype " + e.String() + " is not implemented. Returning a nil" + logs.Error(message) + return nil + } +} diff --git a/models/schedule.go b/models/schedule.go new file mode 100644 index 0000000..d157166 --- /dev/null +++ b/models/schedule.go @@ -0,0 +1,322 @@ +package models + +import ( + "errors" + "time" + + "cloud.o-forge.io/core/oc-catalog/services" + "github.com/beego/beego/v2/core/logs" + "github.com/vk496/cron" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +const MAX_DATES uint = 1000 // Max number of dates to retrieve +const MAX_SCHEDULES int = 200 + +type ScheduleDB struct { + StartDate time.Time + StopDate time.Time + Workflow string + ResourceQty ExecutionRequirementsModel +} + +type ScheduleInfo struct { + Total int + NextExecutions [5]string +} + +type WorkflowSchedule struct { + IsService bool `description:"Service: true, Task: false"` + StartDate time.Time + StopDate time.Time + Cron string `json:"cron"` + Duration uint `json:"duration" description:"Durantion in seconds" example:"7200"` + Events string `json:"events"` + IsBooked bool `json:"isBooked"` +} + +func timeBetween(base, t1, t2 time.Time) bool { + if t2.Before(t1) { + return false + } + + if (t1.Before(base) || t1.Equal(base)) && (t2.After(base) || t2.Equal(base)) { + return true + } else { + return false + } +} + +func CheckSchedule(cronString string, duration uint, cronFirstDate, cronLastDate time.Time, desiredToSchedule ExecutionRequirementsModel) (err error) { + // ######################################## + // TODO: Redesign the whole flow for all possible cases. Meanwhile, use it like this + // ######################################## + // + // Here we have already a timeslot filled by other cron Task. So we must check if there + // is still resources available + + // However, we could have 3 possibilities here: + // + // + // ## Task with start and stop Date in the DB ## + // + // startDate stopDate + // .-----------------------------· + // | | + // ·-----------------------------· + // + // + // New tasks that have conflicts with what we have in the DB + // + // CASE1 (beggining): + // .--------· + // |########W| + // ·--------· + // + // CASE2 (end): + // .--------· + // |########| + // ·--------· + // + // CASE3 (middle): + // .--------· + // |########| + // ·--------· + // + // CASE4 (multiple): + // .-----· .-· .-----· .----------------· + // |#####| |#| |#####| |################| + // ·-----· ·-· ·-----· ·----------------· + // + // + // The first 3 cases are trivial. But in the 4th case, we must get the sum of the resources + // The same could happen in the opposite, where cases are DB entries + + cron, err := cron.Parse(cronString) + if err != nil { + return errors.New("Bad cron message: " + err.Error()) + } + + dcModel := GetDatacenterFromAcronym(services.DC_NAME) + if dcModel == nil { + return errors.New("The DC " + services.DC_NAME + " doesn't have any DC model with that acronym") + } + + if desiredToSchedule.CPUs > dcModel.GetTotalCPUs() { + return errors.New("Requested more CPUs than DC have") + } + if desiredToSchedule.GPUs > dcModel.GetTotalGPUs() { + return errors.New("Requested more GPUs than DC have") + } + if desiredToSchedule.RAM > dcModel.GetTotalRAM() { + return errors.New("Requested more RAM than DC have") + } + + var lastMongoDBdate ScheduleDB + dberr := services.MngoCollSchedule.FindOne(services.MngoCtx, + primitive.M{}, + options.FindOne().SetSort(primitive.D{{"stopdate", -1}}), + ).Decode(&lastMongoDBdate) + + if dberr != nil { + if dberr == mongo.ErrNoDocuments { + // The database is empty. We can book without problems + return + } + return dberr + } + + // for cursor.Next(services.MngoCtx) { + // var item Workspace + // if err = cursor.Decode(&item); err != nil { + // logs.Error(err) + // close(ch) + // } + // } + // var cronScheduleStart, cronScheduleStop time.Time + + // cronScheduleStart = cron.Next(cronFirstDate) // Get the first execution + + for cronScheduleStart := cron.Next(cronFirstDate); !cronScheduleStart.IsZero() && cronScheduleStart.Before(lastMongoDBdate.StopDate); cronScheduleStart = cron.Next(cronScheduleStart) { + cronScheduleStop := cronScheduleStart.Add(time.Second * time.Duration(duration)) + if cronScheduleStop.After(cronLastDate) || cronScheduleStart.Before(cronFirstDate) { + // We skip values that are in the middle of the limits + continue + } + // ########################### + + cursor, err := services.MngoCollSchedule.Find(services.MngoCtx, + primitive.M{"$or": primitive.A{ + primitive.M{"startdate": primitive.M{ + "$gte": cronScheduleStart, + "$lte": cronScheduleStop, + }}, + primitive.M{"stopdate": primitive.M{ + "$gte": cronScheduleStart, + "$lte": cronScheduleStop, + }}, + primitive.M{"$and": primitive.A{ + primitive.M{"startdate": primitive.M{ + "$lte": cronScheduleStart, + }}, + primitive.M{"stopdate": primitive.M{ + "$gte": cronScheduleStop, + }}, + }}, + }}, + // options.Find().SetSort(primitive.D{{"startdate", 1}}), + ) + if err != nil { + return err + } + + var items []ScheduleDB + cursor.All(services.MngoCtx, &items) + + if len(items) == 0 { + // A empty time slot. Available + continue + } + + // There is some workflows booked here. We must check if there is remaining resources + var alreadScheduled ExecutionRequirementsModel + + for _, scheduled := range items { + alreadScheduled.CPUs += scheduled.ResourceQty.CPUs + alreadScheduled.GPUs += scheduled.ResourceQty.GPUs + alreadScheduled.RAM += scheduled.ResourceQty.RAM + } + + if alreadScheduled.CPUs+desiredToSchedule.CPUs > dcModel.GetTotalCPUs() { + return errors.New("Not enough CPU capacity from date " + cronScheduleStart.UTC().String() + " to " + cronScheduleStop.UTC().String()) + } + + if alreadScheduled.GPUs+desiredToSchedule.GPUs > dcModel.GetTotalGPUs() { + return errors.New("Not enough GPU capacity from date " + cronScheduleStart.UTC().String() + " to " + cronScheduleStop.UTC().String()) + } + + if alreadScheduled.RAM+desiredToSchedule.RAM > dcModel.GetTotalRAM() { + return errors.New("Not enough RAM capacity from date " + cronScheduleStart.UTC().String() + " to " + cronScheduleStop.UTC().String()) + } + + } + + return +} + +func CreateScheduleWorkflow(dcName, userID, workflowName, cronString string, duration uint, startDate, stopDate time.Time, requirements ExecutionRequirementsModel) (ret ScheduleInfo, err error) { + + //TODO: Check that dcName is correct + + err = CheckSchedule(cronString, duration, startDate, stopDate, requirements) + if err != nil { + return ret, err + } + + // Already checked possible errors + myCron, _ := cron.Parse(cronString) + + scheduledTimeStart := myCron.Next(time.Now().UTC()) // Get the first execution + + counter := 0 + + var uploadSchedule []interface{} + + for !scheduledTimeStart.IsZero() && counter < MAX_SCHEDULES { + scheduledTimeStop := scheduledTimeStart.Add(time.Second * time.Duration(duration)) + if scheduledTimeStop.After(stopDate) || scheduledTimeStart.Before(startDate) { + // We skip values that are in the middle of the limits + scheduledTimeStart = myCron.Next(scheduledTimeStart) + counter++ + continue + } + + uploadSchedule = append(uploadSchedule, ScheduleDB{ + StartDate: scheduledTimeStart, + StopDate: scheduledTimeStop, + Workflow: dcName + "." + userID + "." + workflowName, + ResourceQty: requirements, //stub + }) + + scheduledTimeStart = myCron.Next(scheduledTimeStart) + counter++ + } + + //FIXME: Consider doing something with the inserting result + _, err = services.MngoCollSchedule.InsertMany(services.MngoCtx, uploadSchedule) + if err != nil { + logs.Error(err) + } + + ret.Total = len(uploadSchedule) + for i := 0; i < 5 && len(uploadSchedule) > i; i++ { + elem := uploadSchedule[i].(ScheduleDB) + ret.NextExecutions[i] = elem.StartDate.String() + } + + return + +} + +func GetFarSchedules(baseDate time.Time, isNext bool) *time.Time { + + operator := "$gt" + if !isNext { + // Previous to this date + operator = "$lt" + } + + var res *ScheduleDB + dberr := services.MngoCollSchedule.FindOne(services.MngoCtx, + primitive.M{"startdate": primitive.M{ + operator: baseDate, + }}, + options.FindOne().SetSort(primitive.D{{"startdate", 1}})).Decode(&res) + + if dberr != nil { + logs.Error(dberr) + return nil + } + + return &res.StartDate +} + +func GetSchedules(startDate, stopDate time.Time) (data []ScheduleDB, maxLimit bool, err error) { + + if startDate.After(stopDate) { + return nil, false, errors.New("stopDate must be after startDate") + } + + // Range of 35 days as max + if startDate.Add(24 * time.Hour * time.Duration(35)).Before(stopDate) { + return nil, false, errors.New("Must be less than 35 days between startDate and stopDate") + } + + //FIXME: Discuss if we should check old schedules + // if startDate.Before(time.Now().UTC()) { + // return nil, false, errors.New("TimeServer is " + time.Now().UTC().String() + " but your startDate is " + startDate.String()) + // } + + firstDateCur, err := services.MngoCollSchedule.Find(services.MngoCtx, + primitive.M{"startdate": primitive.M{ + "$gte": startDate, + "$lte": stopDate, + }}, + options.Find().SetLimit(int64(MAX_DATES)), + ) + + if err != nil { + return + } + + firstDateCur.All(services.MngoCtx, &data) + + if len(data) == int(MAX_DATES) { + maxLimit = true + } + + return + +} diff --git a/models/search.go b/models/search.go new file mode 100644 index 0000000..6e96eab --- /dev/null +++ b/models/search.go @@ -0,0 +1,101 @@ +package models + +import ( + "strings" + + "cloud.o-forge.io/core/oc-catalog/services" + "github.com/beego/beego/v2/core/logs" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +type SearchResult struct { + Computing []ComputingModel `json:"computing" required:"true"` + Datacenter []DatacenterModel `json:"datacenter"` + Storage []StorageModel `json:"storage"` + Data []DataModel `json:"data"` +} + +func FindByWord(word string) (object *SearchResult, err error) { + + returnObject := SearchResult{} + opts := options.Find() + opts.SetLimit(100) //FIXME: Decide if limit and how + + var cursor *mongo.Cursor + if strings.TrimSpace(word) == "*" { + word = "" + } else { + word = `(?i).*` + word + `.*` + } + // filter := bson.M{"$text": bson.M{"$search": word}} + + if cursor, err = services.MngoCollComputing.Find( + services.MngoCtx, + bson.M{"$or": []bson.M{ + {"description": bson.M{"$regex": word}}, + {"owner": bson.M{"$regex": word}}, + {"license": bson.M{"$regex": word}}, + }}, + opts, + ); err != nil { + logs.Error(err) + return nil, err + } + if err = cursor.All(services.MngoCtx, &returnObject.Computing); err != nil { + logs.Error(err) + return nil, err + } + + if cursor, err = services.MngoCollDatacenter.Find( + services.MngoCtx, + bson.M{"$or": []bson.M{ + {"name": bson.M{"$regex": word}}, + {"description": bson.M{"$regex": word}}, + {"owner": bson.M{"$regex": word}}, + }}, opts, + ); err != nil { + logs.Error(err) + return nil, err + } + if err = cursor.All(services.MngoCtx, &returnObject.Datacenter); err != nil { + logs.Error(err) + return nil, err + } + + if cursor, err = services.MngoCollStorage.Find( + services.MngoCtx, + bson.M{"$or": []bson.M{ + {"name": bson.M{"$regex": word}}, + {"description": bson.M{"$regex": word}}, + }}, + opts, + ); err != nil { + logs.Error(err) + return nil, err + } + if err = cursor.All(services.MngoCtx, &returnObject.Storage); err != nil { + logs.Error(err) + return nil, err + } + + if cursor, err = services.MngoCollData.Find( + services.MngoCtx, + bson.M{"$or": []bson.M{ + {"description": bson.M{"$regex": word}}, + {"example": bson.M{"$regex": word}}, + }}, + opts, + ); err != nil { + logs.Error(err) + return nil, err + } + if err = cursor.All(services.MngoCtx, &returnObject.Data); err != nil { + logs.Error(err) + return nil, err + } + + return &returnObject, nil +} diff --git a/models/storage.go b/models/storage.go new file mode 100644 index 0000000..1f04624 --- /dev/null +++ b/models/storage.go @@ -0,0 +1,138 @@ +package models + +import ( + "cloud.o-forge.io/core/oc-catalog/models/rtype" + "cloud.o-forge.io/core/oc-catalog/services" + "github.com/beego/beego/v2/core/logs" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +type StorageNEWModel struct { + Name string `json:"name" required:"true"` + Description string `json:"description" required:"true"` + ShortDescription string `json:"short_description" required:"true" validate:"required"` + Logo string `json:"logo" required:"true" validate:"required"` + Type string `json:"type,omitempty" required:"true"` + + DCacronym string `json:"DCacronym" required:"true" description:"Unique ID of the DC where it is the storage"` + + Size uint `json:"size" required:"true"` + Encryption bool `json:"encryption" ` + Redundancy string `json:"redundancy" ` + Throughput string `json:"throughput" ` + BookingPrice uint `json:"bookingPrice" ` +} + +type StorageModel struct { + ID string `json:"ID" bson:"_id" required:"true"` + StorageNEWModel `bson:",inline"` +} + +func (obj StorageModel) getRtype() rtype.Rtype { + return rtype.STORAGE +} + +func (model StorageModel) getName() string { + return model.Name +} + +type StorageObject struct { + ReferenceID primitive.ObjectID `json:"referenceID" description:"Storage model ID"` + + Inputs []string `json:"inputs" ` + Outputs []string `json:"outputs" ` +} + +func (obj StorageObject) getHost() *string { + return nil // Host is DC only attribute +} + +func (obj StorageObject) getModel() (ret ResourceModel, err error) { + var ret2 StorageModel + res := services.MngoCollStorage.FindOne(services.MngoCtx, + primitive.M{"_id": obj.ReferenceID}, + ) + + if err = res.Err(); err != nil { + return + } + + err = res.Decode(&ret2) + return ret2, err +} + +func (obj *StorageObject) setReference(rID primitive.ObjectID) { + obj.ReferenceID = rID +} + +func (obj StorageObject) getReference() primitive.ObjectID { + return obj.ReferenceID +} + +func (obj StorageObject) getRtype() rtype.Rtype { + return rtype.STORAGE +} + +func (obj StorageObject) getName() (name *string) { + + res := services.MngoCollStorage.FindOne(services.MngoCtx, primitive.M{"_id": obj.ReferenceID}) + + if res.Err() != nil { + logs.Error(res) + return + } + + var ret StorageModel + res.Decode(&ret) + + return &ret.Name +} + +func (obj StorageObject) isLinked(rObjID string) LinkingState { + if contains(obj.Inputs, rObjID) { + return INPUT + } + + if contains(obj.Outputs, rObjID) { + return OUTPUT + } + + return NO_LINK +} + +func (obj *StorageObject) addLink(direction LinkingState, rObjID string) { + switch direction { + case INPUT: + obj.Inputs = append(obj.Inputs, rObjID) + case OUTPUT: + obj.Outputs = append(obj.Outputs, rObjID) + } +} + +func PostOneStorage(obj StorageNEWModel) (string, error) { + return postOneResource(obj, rtype.STORAGE) +} + +func GetOneStorage(ID string) (object *StorageModel, err error) { + obj, err := getOneResourceByID(ID, rtype.STORAGE) + + if err != nil { + return object, err + } + + object = obj.(*StorageModel) + + return object, err +} + +func GetMultipleStorage(IDs []string) (object *[]StorageModel, err error) { + objArray, err := getMultipleResourceByIDs(IDs, rtype.STORAGE) + + if err != nil { + return nil, err + } + + object = objArray.(*[]StorageModel) + + return object, err +} diff --git a/models/user.go b/models/user.go new file mode 100644 index 0000000..6d8e595 --- /dev/null +++ b/models/user.go @@ -0,0 +1,12 @@ +package models + +type UserModel struct { + ID string `json:"id,omitempty",bson:"_id"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + Email string `json:"email,omitempty"` +} + +func Login(username, password string) bool { + return true +} diff --git a/models/workflow.go b/models/workflow.go new file mode 100644 index 0000000..526ee2b --- /dev/null +++ b/models/workflow.go @@ -0,0 +1,997 @@ +package models + +import ( + "context" + "encoding/xml" + "errors" + "sort" + "time" + + "cloud.o-forge.io/core/oc-catalog/models/rtype" + swagger "cloud.o-forge.io/core/oc-catalog/selfapi" + "cloud.o-forge.io/core/oc-catalog/services" + "github.com/beego/beego/v2/core/logs" + "github.com/vk496/cron" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +type LinkingState uint + +// When we check the schedule of a workflow, we report with this +type DCstatus struct { + DCname string + DCobjID string //FIXME: Probably should be model ID + IsReachable bool + + IsAvailable bool + Booked *ScheduleInfo + + ErrorMessage string +} + +const ( + NO_LINK LinkingState = iota + INPUT + OUTPUT +) + +func boolToLinkingState(boolState bool) LinkingState { + switch boolState { + case true: + return INPUT + case false: + return OUTPUT + default: + return NO_LINK + } +} + +const SchedulesDB = "schedules" + +type Workflow struct { + // The key of the map is the ID of the object itself + Data map[string]DataObject `json:"data"` + Computing map[string]ComputingObject `json:"computing"` + Storage map[string]StorageObject `json:"storage"` + Datacenter map[string]DatacenterObject `json:"datacenter"` //TODO: Decide if there should be multiple objects of a datacenter + + Schedules WorkflowSchedule `json:"schedules"` + + MxgraphXML string `description:"State of the mxgraph"` +} + +type ResourceObject interface { + getHost() *string + getName() *string + getModel() (ResourceModel, error) + getRtype() rtype.Rtype + setReference(rObjID primitive.ObjectID) + getReference() primitive.ObjectID + isLinked(rObjID string) LinkingState + addLink(direction LinkingState, rObjID string) +} + +// Get a sum of all execution requirements attached to a DC obj +func (w Workflow) GetExecutionRequirements(dcIDobj string) (ret ExecutionRequirementsModel, err error) { + + // Find the id of the DC obj + + if _, ok := w.Datacenter[dcIDobj]; !ok { + return ExecutionRequirementsModel{}, errors.New("DC obj" + dcIDobj + " doesn't exist in the Workflow") + } + + // Get all elements that are attached to the DC + + for _, computingObj := range w.Computing { + if computingObj.DataCenterID == dcIDobj { + mymodel, err := computingObj.getModel() + if err != nil { + return ExecutionRequirementsModel{}, err + } + + compModel := mymodel.(ComputingModel) + + //TODO a generic way to concatenate execution requirements + ret.CPUs += compModel.ExecutionRequirements.CPUs + ret.GPUs += compModel.ExecutionRequirements.GPUs + ret.RAM += compModel.ExecutionRequirements.RAM + } + } + + return +} + +func (w *Workflow) GetResource(rObjID *string) (retObj ResourceObject) { + + if rObjID == nil { + return nil + } + + if storVal, ok := w.Data[*rObjID]; ok { + retObj = &storVal + return + } + + if storVal, ok := w.Computing[*rObjID]; ok { + retObj = &storVal + return + } + + if storVal, ok := w.Storage[*rObjID]; ok { + retObj = &storVal + return + } + + if storVal, ok := w.Datacenter[*rObjID]; ok { + retObj = &storVal + return + } + + return nil +} + +func (w *Workflow) GetResourceMapByRtype(rt rtype.Rtype) interface{} { + switch rt { + case rtype.DATA: + return w.Data + case rtype.COMPUTING: + return w.Computing + case rtype.STORAGE: + return w.Storage + case rtype.DATACENTER: + return w.Datacenter + default: + return nil + } +} + +func (w *Workflow) CreateResourceObject(rt rtype.Rtype) ResourceObject { + var res ResourceObject + switch rt { + case rtype.DATA: + res = &DataObject{} + case rtype.COMPUTING: + res = &ComputingObject{} + case rtype.STORAGE: + res = &StorageObject{} + case rtype.DATACENTER: + res = &DatacenterObject{} + default: + res = nil + } + return res +} + +func (w *Workflow) AddObj(robj ResourceObject) *primitive.ObjectID { + outputID := primitive.NewObjectID() + w.UpdateObj(robj, outputID.Hex()) + + return &outputID +} + +func (w *Workflow) UpdateDB(userID, workflowName string) error { + + _, err := services.MngoCollWorkspace.UpdateOne(services.MngoCtx, + primitive.M{"_id": userID}, + primitive.M{"$set": primitive.M{WorkflowDB + "." + workflowName: w}}, + ) + + return err +} + +func (w *Workflow) UpdateObj(robj ResourceObject, objID string) { + switch robj.getRtype() { + case rtype.DATA: + var target DataObject + if w.Data == nil { + //init + w.Data = make(map[string]DataObject) + } + target = *robj.(*DataObject) + w.Data[objID] = target + + case rtype.COMPUTING: + var target ComputingObject + if w.Computing == nil { + //init + w.Computing = make(map[string]ComputingObject) + } + target = *robj.(*ComputingObject) + w.Computing[objID] = target + + case rtype.STORAGE: + var target StorageObject + if w.Storage == nil { + //init + w.Storage = make(map[string]StorageObject) + } + target = *robj.(*StorageObject) + w.Storage[objID] = target + case rtype.DATACENTER: + var target DatacenterObject + if w.Datacenter == nil { + //init + w.Datacenter = make(map[string]DatacenterObject) + } + target = *robj.(*DatacenterObject) + w.Datacenter[objID] = target + } + +} + +func GetWorkflow(userID, workflowName string) (workflow *Workflow, err error) { + + userWorkspace := GetWorkspace(userID) + + if userWorkspace != nil { + if theWorkflow, ok := userWorkspace.Workflows[workflowName]; ok { + return &theWorkflow, nil + } + logs.Debug("No workflow name") + return nil, errors.New("No workflow name") + } + + logs.Debug("No workspace") + return nil, errors.New("No workspace") +} + +func CreateWorkflow(userID, workflowName string) (err error) { + + //TODO: Maybe operate directly in the DB instead retriving the full object? + userWorkspace := GetWorkspace(userID) + + // Exist in the DB + if userWorkspace != nil { + + if _, ok := userWorkspace.Workflows[workflowName]; ok { + message := "Workspace workflow " + workflowName + + " is already created for user " + userID + logs.Debug(message) + return errors.New(message) + } + + userWP := &Workflow{} + + // New element + addElem := primitive.M{WorkflowDB + "." + workflowName: userWP} + + // If user doesn't have workflows, we must init at least one + if userWorkspace.Workflows == nil { + addElem = primitive.M{WorkflowDB: map[string]*Workflow{ + workflowName: userWP, + }} + } + + _, err := services.MngoCollWorkspace.UpdateOne(services.MngoCtx, + primitive.M{"_id": userID}, + primitive.M{"$set": addElem}, + ) + + if err != nil { + message := "Internal error when updating in DB" + logs.Debug(message + "; " + err.Error()) + return errors.New(message) + } + return nil + } + + return errors.New("Can't create a workflow without a workspace") +} + +func CreateObjectInWorkflow(userID, workflowName, rID string) (rObjID2 *string, err error) { + + userWorkspace := GetWorkspace(userID) + + if userWorkspace == nil { + return nil, errors.New("No workspace for user " + userID) + } + + if _, ok := userWorkspace.Workflows[workflowName]; !ok { + return nil, errors.New("Workspace workflow " + workflowName + " doesn't exist for user " + userID) + } + + rIDObj, err := primitive.ObjectIDFromHex(rID) + + if err != nil { + return nil, errors.New("ID " + rID + " is not valid") + } + + //TODO: We are replacing the entire array instead of pushing + // a new element. Probably will have problems with multithread/async + // operations and consistency in the future + + for rtyp, resource := range userWorkspace.GetResources() { + if contains(resource, rID) { + wWorkflow := userWorkspace.Workflows[workflowName] + + newObj := wWorkflow.CreateResourceObject(rtyp) + newObj.setReference(rIDObj) + + outputID := wWorkflow.AddObj(newObj) + + _, err := services.MngoCollWorkspace.UpdateOne(services.MngoCtx, + primitive.M{"_id": userID}, + primitive.M{"$set": primitive.M{WorkflowDB + "." + workflowName + "." + rtyp.String(): wWorkflow.GetResourceMapByRtype(rtyp)}}, + ) + + if err != nil { + return nil, errors.New("Internal error when updating in DB: " + err.Error()) + } + + outStr := outputID.Hex() + + return &outStr, nil + } + } + + return nil, errors.New("rID " + rID + " doesn't exist in the user workspace") +} + +func LinkObjectsInWorkspace(userID, workflowName, rObjIDsource string, isInput bool, rObjIDtarger string) (err error) { + userWorkspace := GetWorkspace(userID) + + if userWorkspace == nil { + return errors.New("No workspace for user " + userID) + } + + if _, ok := userWorkspace.Workflows[workflowName]; !ok { + return errors.New("Workspace workflow " + workflowName + " doesn't exist for user " + userID) + } + + // Check rObjIDsource + if _, ok := userWorkspace.Workflows[workflowName].Data[rObjIDsource]; !ok { + return errors.New("rObjIDsource must be of type DATA for now") + } + + // Check rObjIDtarger + + wWorkflow := userWorkspace.Workflows[workflowName] + + resObjTarget := wWorkflow.GetResource(&rObjIDtarger) + + if resObjTarget == nil { + return errors.New("rObjIDtarger doesn't exist") + } else if resObjTarget.getRtype() == rtype.DATA { + return errors.New("rObjIDtarger of type Data doesn't have inputs/outputs") + } + + if resObjTarget.isLinked(rObjIDsource) != NO_LINK { + return errors.New("rObjIDsource already exists in the inputs or outputs") + } + + resObjTarget.addLink(boolToLinkingState(isInput), rObjIDsource) + + _, err = services.MngoCollWorkspace.UpdateOne(services.MngoCtx, + primitive.M{"_id": userID}, + primitive.M{"$set": primitive.M{ + WorkflowDB + "." + + workflowName + "." + + resObjTarget.getRtype().String() + "." + + rObjIDtarger: resObjTarget}}, + ) + + if err != nil { + return errors.New("Internal error when updating in DB: " + err.Error()) + } + + return nil +} + +func GetWorkflowSchedule(username, workflowName string) (Currentschedule *WorkflowSchedule, err error) { + + userWorkspace := GetWorkspace(username) + + if userWorkspace == nil { + return nil, errors.New("No workspace for user " + username) + } + + if workflow, ok := userWorkspace.Workflows[workflowName]; ok { + Currentschedule = &workflow.Schedules + } + + return +} + +func SetWorkflowSchedule(username, workflowName, cronString, events string, isService bool, startDate, stopDate time.Time, duration uint) (NextSchedules *ScheduleInfo, err error) { + + userWorkspace := GetWorkspace(username) + + if userWorkspace == nil { + return nil, errors.New("No workspace for user " + username) + } + + // Check if workflow exist + if _, ok := userWorkspace.Workflows[workflowName]; !ok { + return + } + + // We mustn't modify a booked schedule + if userWorkspace.Workflows[workflowName].Schedules.IsBooked { + return nil, errors.New("A booked schedule can't be modified") + } + + sch := WorkflowSchedule{} + NextSchedules = &ScheduleInfo{} + + sch.StartDate = startDate + sch.StopDate = stopDate + sch.IsService = isService + + if isService { // Service + NextSchedules.NextExecutions[0] = startDate.String() + NextSchedules.Total = 1 + } + + if !isService { // Task + sch.Cron = cronString + sch.Duration = duration + sch.Events = events + + // Obtain next executions + counter := 0 + myCron, _ := cron.Parse(cronString) // NOTE: already checked in the controller + scheduledStart := myCron.Next(startDate) // Get the first execution starting from startDate + + for !scheduledStart.IsZero() && counter < MAX_SCHEDULES { + scheduleStop := scheduledStart.Add(time.Second * time.Duration(duration)) + if scheduleStop.After(stopDate) || scheduledStart.Before(startDate) { + // If a task is longer than last possible date, we ignore it + scheduledStart = myCron.Next(scheduledStart) + counter++ + continue + } + + if counter < len(NextSchedules.NextExecutions) { + NextSchedules.NextExecutions[counter] = scheduledStart.String() + } + + scheduledStart = myCron.Next(scheduledStart) + counter++ + } + + NextSchedules.Total = counter + + if NextSchedules.Total == 0 { + return nil, errors.New("Current Task configuration will have 0 executions") + } + } + + _, err = services.MngoCollWorkspace.UpdateOne(services.MngoCtx, + primitive.M{"_id": username}, + primitive.M{"$set": primitive.M{ + WorkflowDB + "." + + workflowName + "." + + SchedulesDB: &sch}}, + ) + + if err != nil { + return nil, errors.New("Internal error when updating in DB: " + err.Error()) + } + + return +} + +func GetMxGraph(username, workflowName string) (xmlData *string, err error) { + userWorkspace := GetWorkspace(username) + + if userWorkspace == nil { + return nil, errors.New("No workspace for user " + username) + } + + if _, ok := userWorkspace.Workflows[workflowName]; !ok { + return nil, errors.New("Workspace workflow " + workflowName + " doesn't exist for user " + username) + } + + var data string = userWorkspace.Workflows[workflowName].MxgraphXML + + if data == "" { + xmlData = nil + } else { + xmlData = &data + } + return +} + +func ParseMxGraph(username, workflowName, xmlData string) (err error, mxissues []error) { + + userWorkspace := GetWorkspace(username) + + if userWorkspace == nil { + return errors.New("No workspace for user " + username), nil + } + + currentWorkflow, ok := userWorkspace.Workflows[workflowName] + if !ok { + return errors.New("No workflow " + workflowName), nil + } + + if currentWorkflow.Schedules.IsBooked { + return errors.New("Can't modify a booked workflow"), nil + } + + var xmlModel MxGraphModel + + // logs.Debug(xmlData) + err = xml.Unmarshal([]byte(xmlData), &xmlModel) + if err != nil { + return err, nil + } + + targetWorkspaceWorkflow, err, mxissues := userWorkspace.ConsumeMxGraphModel(xmlModel) + if err != nil { + return err, nil + } + + targetWorkspaceWorkflow.MxgraphXML = xmlData + targetWorkspaceWorkflow.Schedules = currentWorkflow.Schedules //TODO: Probably we should move schudles outside the workflow + + _, err = services.MngoCollWorkspace.UpdateOne(services.MngoCtx, + primitive.M{"_id": username}, + primitive.M{"$set": primitive.M{ + WorkflowDB + "." + + workflowName: targetWorkspaceWorkflow}}, + ) + + if err != nil { + return errors.New("Internal error when updating in DB: " + err.Error()), nil + } + + return nil, mxissues +} + +// FindInSlice takes a slice and looks for an element in it. If found it will +// return it's key, otherwise it will return -1 and a bool of false. +func FindInSlice(slice []string, val string) (int, bool) { + for i, item := range slice { + if item == val { + return i, true + } + } + return -1, false +} + +// At least one element exist in both slices +// Return index1, index2 and if exist +func FindSliceInSlice(slice1 []string, slice2 []string) (int, int, bool) { + for i1, item1 := range slice1 { + for i2, item2 := range slice2 { + if item1 == item2 { + return i1, i2, true + } + } + } + return -1, -1, false +} + +func (w Workspace) ConsumeMxGraphModel(xmlmodel MxGraphModel) (ret *Workflow, err error, issues []error) { + + ret = &Workflow{} + + // When we will iterate over the full array of cells, we first will register the resources + // and after the linkage between them + sort.Slice(xmlmodel.Root.MxCell, func(i, j int) bool { + return xmlmodel.Root.MxCell[i].RID != nil + }) + + for _, cell := range xmlmodel.Root.MxCell { + + switch { + case cell.RID != nil: + // Case of a Resource + rType := w.getRtype(*cell.RID) + + if rType == rtype.INVALID { + return nil, + errors.New("Refering to a rID that is not in the workflow"), + nil + } + + // Generate ObjectID for the reference ID + rIDObj, err := primitive.ObjectIDFromHex(*cell.RID) + if err != nil { + return nil, + errors.New("Bad ID format: " + *cell.RID), + nil + } + + resObj := ret.CreateResourceObject(rType) + resObj.setReference(rIDObj) + + ret.UpdateObj(resObj, cell.ID) + case cell.ID == "0" || cell.ID == "1": + // ID 0 and 1 are special cases of mxeditor + continue + // issues = append(issues, errors.New("MxCell with ID "+cell.ID+" doesn't have a valid link")) + + default: + // Not root nor resource. Should be only links + sourceObj := ret.GetResource(cell.Source) + targetObj := ret.GetResource(cell.Target) + + if sourceObj == nil || targetObj == nil { + if sourceObj == nil && targetObj == nil { + issues = append(issues, errors.New("Arrow "+cell.ID+" is alone")) + } else if sourceObj == nil { + issues = append(issues, errors.New("Arrow ("+cell.ID+") to "+*targetObj.getName()+" without parent")) + } else { + issues = append(issues, errors.New("Arrow "+cell.ID+" from "+*sourceObj.getName()+" without target")) + } + + // If is a invalid link, we can't save it in the DB + continue + } + + if sourceObj.getRtype() == rtype.DATACENTER || targetObj.getRtype() == rtype.DATACENTER { + var datacenter, datacenterLinked *string + + if sourceObj.getRtype() == rtype.DATACENTER { + datacenter = cell.Source + datacenterLinked = cell.Target + } else { + datacenter = cell.Target + datacenterLinked = cell.Source + } + + switch ret.GetResource(datacenterLinked).getRtype() { + case rtype.COMPUTING: + computingObj := ret.GetResource(datacenterLinked).(*ComputingObject) + + // We should always get a ID because we already registered resources and discarded which doesn't correspond to existent models + computingObj.DataCenterID = *datacenter + ret.UpdateObj(computingObj, *datacenterLinked) + } + + } else { + targetObj.addLink(INPUT, *cell.Source) + ret.UpdateObj(targetObj, *cell.Target) // save back + + // If we have a relationship of: + // Source ----> Target + // + // The Source will be in the INPUTs of the Target. + // But we also must make sure that the Target will be in the OUTPUTs of the Source + + sourceObj.addLink(OUTPUT, *cell.Target) + ret.UpdateObj(sourceObj, *cell.Source) + } + + } + } + + dcslist := make(map[string]bool) + dataslist := make(map[string]bool) + // datalist := make(map[string]bool) + + for _, comp := range ret.Computing { + if comp.DataCenterID == "" { + issues = append(issues, errors.New("Computing "+*comp.getName()+" without a Datacenter")) + } else { + // If doesn't exist in the list, means is new element to register as used + dcslist[comp.DataCenterID] = true + + } + + for _, dcin := range comp.Inputs { + switch ret.GetResource(&dcin).getRtype() { + case rtype.DATA: + dataslist[dcin] = true + } + } + + for _, dcout := range comp.Outputs { + switch ret.GetResource(&dcout).getRtype() { + case rtype.DATA: + dataslist[dcout] = true + } + } + + } + + for _, va := range ret.Storage { + if va.Inputs == nil && va.Outputs == nil { + issues = append(issues, errors.New("Storage "+*va.getName()+" without compatible inputs and outputs")) + } + } + + for dcID, va := range ret.Datacenter { + // if rID doesn't exist in the list, it means that it's not used + if _, ok := dcslist[dcID]; !ok { + issues = append(issues, errors.New("DC "+*va.getName()+" not atached to any Computing")) + } + } + + for dcID, va := range ret.Data { + // if rID doesn't exist in the list, it means that it's not used + if _, ok := dataslist[dcID]; !ok { + issues = append(issues, errors.New("Data "+*va.getName()+" not atached to any Computing")) + } + } + + ////////////////////////////////////////////////////////// + // // + // Starting from here, we check the type of resources // + // // + ////////////////////////////////////////////////////////// + + // FIXME: Avoid checking twice the same cases (cycles). Ex: + // + // Comp1 ----> Comp2 + // + // In this case, we will check Comp1 outputs with Comp2 + // inputs AND Comp2 inputs with Comp1 outputs, since we are + // iterating over all existent Computing models in the Graph + + for _, comp := range ret.Computing { + + compModel, err2 := comp.getModel() + if err = err2; err != nil { + return + } + + currentCompModel := compModel.(ComputingModel) + + // Come computings may not allow inputs or outputs + if len(currentCompModel.Dinputs) == 0 && len(comp.Inputs) > 0 { + issues = append(issues, errors.New("Computing "+compModel.getName()+" must not have any input")) + continue + } + + if len(currentCompModel.Doutputs) == 0 && len(comp.Outputs) > 0 { + issues = append(issues, errors.New("Computing "+compModel.getName()+" must not have any output")) + continue + } + + //TODO: We should allow heterogenous inputs? + for _, objIn := range comp.Inputs { + resIn := ret.GetResource(&objIn) + resInType := resIn.getRtype() + switch resInType { + case rtype.DATA: + + dataModel, err2 := resIn.getModel() + if err = err2; err != nil { + return + } + myDataModel := dataModel.(DataModel) + + if _, ok := FindInSlice(currentCompModel.Dinputs, myDataModel.Dtype); !ok { + issues = append(issues, errors.New("Computing "+compModel.getName()+" can't handle inputs of type "+myDataModel.Dtype+" from Data "+dataModel.getName())) + } + + case rtype.COMPUTING: + inCompModel, err2 := resIn.getModel() + if err = err2; err != nil { + return + } + + myInComputingModel := inCompModel.(ComputingModel) + + if _, _, ok := FindSliceInSlice(myInComputingModel.Doutputs, currentCompModel.Dinputs); !ok { + issues = append(issues, errors.New("Computing "+compModel.getName()+" can't handle any input from "+inCompModel.getName())) + } + case rtype.STORAGE: + // Storage can give use anything, so we always accept it's input for now + continue + + default: + issues = append(issues, errors.New("Computing "+currentCompModel.getName()+" can't have any resource of type "+resInType.String()+" (behaviour not defined)")) + } + } + + //TODO: We should allow heterogenous outputs? + for _, objOut := range comp.Outputs { + resOut := ret.GetResource(&objOut) + resOutType := resOut.getRtype() + switch resOutType { + case rtype.COMPUTING: + outCompModel, err2 := resOut.getModel() + if err = err2; err != nil { + return + } + + myOutComputingModel := outCompModel.(ComputingModel) + + if _, _, ok := FindSliceInSlice(currentCompModel.Doutputs, myOutComputingModel.Dinputs); !ok { + issues = append(issues, errors.New("Computing "+compModel.getName()+" doesn't have output data compatible with "+outCompModel.getName())) + } + case rtype.STORAGE: + // Storage can save anything, so we always accept store it for now + continue + + default: + issues = append(issues, errors.New("Computing "+currentCompModel.getName()+" can't have any resource of type "+resOutType.String()+" (behaviour not defined)")) + } + } + + } + + return +} + +func sumExecutionReqs(exeqReq ...ExecutionRequirementsModel) (ret ExecutionRequirementsModel) { + for _, v := range exeqReq { + ret.CPUs += v.CPUs + ret.GPUs += v.GPUs + ret.RAM += v.RAM + } + + return +} + +func CheckAndBookWorkflowSchedule(username, workflowName string, book bool) (myRet []DCstatus, err error) { + + userWorkspace := GetWorkspace(username) + + if userWorkspace == nil { + return nil, errors.New("No workspace for user " + username) + } + + currentWorkflow, ok := userWorkspace.Workflows[workflowName] + if !ok { + return nil, errors.New("No workflow " + workflowName + " for user " + username) + } + + if currentWorkflow.Schedules.IsBooked { + return nil, errors.New("Can't operate DCs for a already booked schedule") + } + + // dd := ¤tWorkflow + + // We can have multiple DCobjs pointing to the same DCmodel. We must sum all req of the same DCmodel + totalDCs := make(map[primitive.ObjectID]ExecutionRequirementsModel) + + for dcIDobj, dcObj := range currentWorkflow.Datacenter { + modelID := dcObj.getReference() + + var totalsModel ExecutionRequirementsModel + totalsModel, err = currentWorkflow.GetExecutionRequirements(dcIDobj) + if err != nil { + return + } + + if _, ok := totalDCs[modelID]; ok { + totalDCs[modelID] = sumExecutionReqs(totalDCs[modelID], totalsModel) + } else { + totalDCs[modelID] = totalsModel + } + } + + myRet = make([]DCstatus, len(totalDCs)) + var i int + i = -1 + for modelID, execReq := range totalDCs { + i++ + var dcModel *DatacenterModel + dcModel, err = GetOneDatacenter(modelID.Hex()) + + if err != nil { + return + } + + myRet[i].DCname = dcModel.Name + myRet[i].DCobjID = modelID.Hex() + + // retrieve the host of the DC + host := GetHost(dcModel.Hosts) + if host == nil { + myRet[i].ErrorMessage = "Datacenter " + myRet[i].DCname + " doesn't have a host property" + continue + } + + cli := services.GetSelfAPI(*host) + data, err := cli.ScheduleApi.ScheduleControllerCheckIfScheduleCanBeCreatedInThisDC(context.Background(), + currentWorkflow.Schedules.Cron, + int32(currentWorkflow.Schedules.Duration), + currentWorkflow.Schedules.StartDate.Format(time.RFC3339), + currentWorkflow.Schedules.StopDate.Format(time.RFC3339), + swagger.ModelsExecutionRequirementsModel{ + Cpus: int32(execReq.CPUs), + Gpus: int32(execReq.GPUs), + Ram: int32(execReq.RAM), + }, + ) + + if err != nil { + myRet[i].ErrorMessage = err.Error() + swErr, ok := err.(swagger.GenericSwaggerError) + if ok { + myRet[i].IsReachable = true + myRet[i].ErrorMessage += ": " + string(swErr.Body()) + } + continue + } + + myRet[i].IsReachable = true + + if data.StatusCode == 200 { + myRet[i].IsAvailable = true + } + + } + + // If we only check, we should exit here + if !book { + return + } + + for _, v := range myRet { + if !v.IsAvailable { + return + } + } + + i = -1 + allBooked := true + for modelID, execReq := range totalDCs { + i++ + // _ = v + var dcModel *DatacenterModel + dcModel, err = GetOneDatacenter(modelID.Hex()) + + if err != nil { + myRet[i].ErrorMessage = err.Error() + continue + } + + cli := services.GetSelfAPI(*GetHost(dcModel.Hosts)) // If we are here, we already check that host exists and is reachable + data, resp, err := cli.ScheduleApi.ScheduleControllerCreateSchedule(context.Background(), + services.DC_NAME, + workflowName, + currentWorkflow.Schedules.Cron, + int32(currentWorkflow.Schedules.Duration), + currentWorkflow.Schedules.StartDate.Format(time.RFC3339), + currentWorkflow.Schedules.StopDate.Format(time.RFC3339), + swagger.ModelsExecutionRequirementsModel{ + Cpus: int32(execReq.CPUs), + Gpus: int32(execReq.GPUs), + Ram: int32(execReq.RAM), + }, + ) + + if err != nil { + allBooked = false + myRet[i].ErrorMessage = err.Error() + swErr, ok := err.(swagger.GenericSwaggerError) + if ok { + myRet[i].IsReachable = true + myRet[i].ErrorMessage += ": " + string(swErr.Body()) + } + continue + } + + if resp.StatusCode == 200 { + //FIXME: Maybe some better way of casting? + + var nextExec [5]string + for counter := 0; counter < 5; counter++ { + nextExec[counter] = data.NextExecutions[counter] + } + + myRet[i].Booked = &ScheduleInfo{ + Total: int(data.Total), + NextExecutions: nextExec, + } + } + + } + + // If some DC fail, we must not mark the workflow as booked + if !allBooked { + return + } + + currentWorkflow.Schedules.IsBooked = true + _, err = services.MngoCollWorkspace.UpdateOne(services.MngoCtx, + primitive.M{"_id": username}, + primitive.M{"$set": primitive.M{ + WorkflowDB + "." + + workflowName + "." + + SchedulesDB: ¤tWorkflow.Schedules}}, + ) + + if err != nil { + logs.Critical("Internal error when updating in DB: " + err.Error()) + } + + return myRet, nil +} diff --git a/models/workspace.go b/models/workspace.go new file mode 100644 index 0000000..c17964b --- /dev/null +++ b/models/workspace.go @@ -0,0 +1,418 @@ +package models + +import ( + "context" + "errors" + + "cloud.o-forge.io/core/oc-catalog/models/rtype" + "cloud.o-forge.io/core/oc-catalog/services" + "github.com/beego/beego/v2/core/logs" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" +) + +// Assure consistency by using a const which refers to the MongoDB entry name +// Workspace.Projects +const WorkflowDB = "workflows" + +type Workspace struct { + UserID string `bson:"_id" json:"user_id"` + + Workflows map[string]Workflow //WorkflowDB + + // ID: rtype + Data []string `json:"data"` + Computing []string `json:"computing"` + Datacenter []string `json:"datacenter"` + Storage []string `json:"storage"` +} + +type ResourceModel interface { + getRtype() rtype.Rtype + getName() string +} + +func (w Workspace) getRtype(rID string) (resModel rtype.Rtype) { + + for _, compVal := range w.Computing { + if compVal == rID { + return rtype.COMPUTING + } + } + + for _, datVal := range w.Data { + if datVal == rID { + return rtype.DATA + } + } + + for _, storVal := range w.Storage { + if storVal == rID { + return rtype.STORAGE + } + } + + for _, datcentVal := range w.Datacenter { + if datcentVal == rID { + return rtype.DATACENTER + } + } + + return rtype.INVALID +} + +func (w *Workspace) GetResources() map[rtype.Rtype][]string { + return map[rtype.Rtype][]string{ + rtype.DATA: w.Data, + rtype.COMPUTING: w.Computing, + rtype.STORAGE: w.Storage, + rtype.DATACENTER: w.Datacenter, + } +} + +func (w *Workspace) GetWorkflow(workflowName string) *Workflow { + + var proj Workflow + + proj = w.Workflows[workflowName] + return &proj +} + +func (w *Workspace) GetWorkflows() []string { + + if len(w.Workflows) == 0 { + return nil + } + + workflowNames := make([]string, len(w.Workflows)) + + i := 0 + for k := range w.Workflows { + workflowNames[i] = k + i++ + } + + return workflowNames +} + +type WorkspaceModel struct { + UserID string `bson:"_id" json:"user_id"` + + Data []DataModel `json:"data"` + Computing []ComputingModel `json:"computing"` + Datacenter []DatacenterModel `json:"datacenter"` + Storage []StorageModel `json:"storage"` +} + +func ListFullWorkspace(userID string) (*WorkspaceModel, error) { + ws := GetWorkspace(userID) + + if ws == nil { + return nil, errors.New("Internal error") + } + + fws := &WorkspaceModel{ + UserID: ws.UserID, + + Data: []DataModel{}, + Computing: []ComputingModel{}, + Datacenter: []DatacenterModel{}, + Storage: []StorageModel{}, + } + + pipeline := []primitive.M{ + {"$match": primitive.M{"_id": userID}}, + {"$lookup": primitive.M{ + "localField": "data", + "from": services.MngoNamesCollection.DATA, + "foreignField": "_id", + "as": "data", + }}, + {"$lookup": primitive.M{ + "localField": "computing", + "from": services.MngoNamesCollection.COMPUTING, + "foreignField": "_id", + "as": "computing", + }}, + {"$lookup": primitive.M{ + "localField": "datacenter", + "from": services.MngoNamesCollection.DATACENTER, + "foreignField": "_id", + "as": "datacenter", + }}, + {"$lookup": primitive.M{ + "localField": "storage", + "from": services.MngoNamesCollection.STORAGE, + "foreignField": "_id", + "as": "storage", + }}, + } + + ret, err := services.MngoCollWorkspace.Aggregate(services.MngoCtx, pipeline) + + if err != nil { + message := "Couldn't obtain subobjects" + logs.Debug(message + "; " + err.Error()) + return nil, errors.New(message) + } + + if ret.RemainingBatchLength() == 1 { + ret.Next(context.Background()) + ret.Decode(&fws) + } + + return fws, nil +} + +// Contains tells whether a contains x. +func contains(a []string, x string) bool { + for _, n := range a { + if x == n { + return true + } + } + return false +} + +func RemoveResource(userID, rID, rType string) error { + + rIDObj, err := primitive.ObjectIDFromHex(rID) + + if err != nil { + message := "ID " + rID + " is not valid" + logs.Debug(message + "; " + err.Error()) + return errors.New(message) + } + + result, err := services.MngoCollWorkspace.UpdateOne(services.MngoCtx, + primitive.M{"_id": userID}, + primitive.M{"$pull": primitive.M{rType: rIDObj}}, + ) + + if err != nil { + message := err.Error() + logs.Debug(message) + return errors.New(message) + } + + if result.MatchedCount == 0 { + message := "No user " + userID + " in workspace" + logs.Debug(message) + return errors.New(message) + } + + if result.ModifiedCount == 0 { + message := "No rID " + rID + " in rtype " + rType + logs.Debug(message) + return errors.New(message) + } + + return nil + +} + +func (w *Workspace) updateDB() (err error) { + + _, err = services.MngoCollWorkspace.ReplaceOne(services.MngoCtx, + primitive.M{"_id": w.UserID}, + w, + ) + + return +} + +func (w *Workspace) NewResource(rID string, rType string) (err error) { + + var targetArray *[]string + + switch rType { + case rtype.DATA.String(): + targetArray = &w.Data + case rtype.COMPUTING.String(): + targetArray = &w.Computing + case rtype.STORAGE.String(): + targetArray = &w.Storage + case rtype.DATACENTER.String(): + targetArray = &w.Datacenter + default: + return errors.New("Rtype " + rType + " is not valid") + } + + for _, models := range *targetArray { + if models == rID { + return errors.New("Resource " + rID + " of type " + rType + + " is already registered for user " + w.UserID) + } + } + + *targetArray = append(*targetArray, rID) + + w.updateDB() + + return +} + +func AddResource(userID, rID, rType string) (err error) { + + var rIDObj *primitive.ObjectID + + if rIDObj, err = IsValidResource(rID, rType); err != nil { + return err + } + + //TODO: Maybe operate directly in the DB instead retriving the full object? + userWorkspace := GetWorkspace(userID) + + // Exist in the DB + if userWorkspace != nil { + var targetArray []string + + switch rType { + case rtype.DATA.String(): + targetArray = userWorkspace.Data + case rtype.COMPUTING.String(): + targetArray = userWorkspace.Computing + case rtype.STORAGE.String(): + targetArray = userWorkspace.Storage + case rtype.DATACENTER.String(): + targetArray = userWorkspace.Datacenter + default: + message := "Rtype " + rType + " is not valid" + logs.Debug(message) + return errors.New(message) + } + + if ok := contains(targetArray, rID); ok { + // Element already registered + message := "Resource " + rID + " of type " + rType + + " is already registered for user " + userID + logs.Debug(message) + return errors.New(message) + } + + // New element + // userWorkspace.ResourceList[rID] = rType + _, err := services.MngoCollWorkspace.UpdateOne(services.MngoCtx, + primitive.M{"_id": userID}, + primitive.M{"$push": primitive.M{rType: rIDObj}}, + ) + + if err != nil { + message := "Internal error when updating in DB" + logs.Debug(message + "; " + err.Error()) + return errors.New(message) + } + + return nil + + } + return errors.New("Internal error") +} + +func rTypeToCollection(rType string) (*mongo.Collection, error) { + switch rType { + case rtype.DATA.String(): + return services.MngoCollData, nil + case rtype.COMPUTING.String(): + return services.MngoCollComputing, nil + case rtype.DATACENTER.String(): + return services.MngoCollDatacenter, nil + case rtype.STORAGE.String(): + return services.MngoCollStorage, nil + } + + message := rType + " is not a valid resource type" + logs.Debug(message) + + return nil, errors.New(message) +} + +func IsValidResource(rID, rType string) (*primitive.ObjectID, error) { + + targetColl, err := rTypeToCollection(rType) + + if err != nil { + return nil, err + } + + rIDObj, err := primitive.ObjectIDFromHex(rID) + + if err != nil { + message := "ID " + rID + " is not valid" + logs.Debug(message + "; " + err.Error()) + return nil, errors.New(message) + } + + result := targetColl.FindOne(services.MngoCtx, primitive.M{"_id": rIDObj}) + + if result.Err() != nil { + message := "ID " + rID + " doesn't exist for resource type " + rType + logs.Debug(message + "; " + result.Err().Error()) + return nil, errors.New(message) + } + + return &rIDObj, nil +} + +func GetAllWorkspaces() <-chan *Workspace { + ch := make(chan *Workspace) + go func() { + cursor, err := services.MngoCollWorkspace.Find(services.MngoCtx, primitive.M{}) + if err != nil { + logs.Error(cursor.Err()) + close(ch) + } + + for cursor.Next(services.MngoCtx) { + var item Workspace + if err = cursor.Decode(&item); err != nil { + logs.Error(err) + close(ch) + } + ch <- &item + } + close(ch) // Remember to close or the loop never ends! + }() + return ch +} + +func (w *Workspace) GetAllWorkspacesProjects() <-chan *Workflow { + ch := make(chan *Workflow) + go func() { + for _, wproj := range w.Workflows { + ch <- &wproj + } + close(ch) + }() + return ch +} + +func GetWorkspace(userID string) (retObj *Workspace) { + + if err := services.MngoCollWorkspace.FindOne(services.MngoCtx, primitive.M{"_id": userID}).Decode(&retObj); err != nil { + logs.Error(err.Error()) + return nil + } + + return +} + +func NewWorkspace(userID string) (*Workspace, error) { + + newWsp := &Workspace{ + UserID: userID, + Data: []string{}, + Computing: []string{}, + Datacenter: []string{}, + Storage: []string{}, + } + + _, err := services.MngoCollWorkspace.InsertOne(services.MngoCtx, newWsp) + if err != nil { + logs.Warning(err.Error()) + return nil, err + } + + return newWsp, nil + +} diff --git a/routers/auth.go b/routers/auth.go new file mode 100644 index 0000000..d08278c --- /dev/null +++ b/routers/auth.go @@ -0,0 +1,118 @@ +package routers + +import ( + "reflect" + "strings" + + "cloud.o-forge.io/core/oc-catalog/controllers" + "cloud.o-forge.io/core/oc-catalog/models" + "github.com/beego/beego/v2/core/logs" + beego "github.com/beego/beego/v2/server/web" + "github.com/beego/beego/v2/server/web/context" + "github.com/beego/beego/v2/server/web/swagger" +) + +func setStatus403(ctx *context.Context) { + ctx.Output.SetStatus(403) + ctx.Output.Body([]byte("")) +} + +// TODO: Force swagger regeneration during startup to ensure consistency +func initAuthMiddleware() { + var FilterAuthTags = func(ctx *context.Context) { + patternMatch := ctx.Input.GetData("RouterPattern").(string) + + patternMatchPath := strings.Replace(patternMatch, rootapi.BasePath, "", 1) + + //Discovery path + if patternMatch == "/" { + return + } + + // First letter Uppercase and the rest lowercase + reqMethod := strings.ToUpper(string(ctx.Request.Method[0])) + strings.ToLower(string(ctx.Request.Method[1:])) + + val := reflect.ValueOf(rootapi.Paths[patternMatchPath]). + Elem().FieldByName(reqMethod). + Elem().Interface().(swagger.Operation) + + // Make sure never omit a security declaration + canaryVar := false + + for _, securityItem := range val.Security { + canaryVar = true + + for securityItemName := range securityItem { + switch t := rootapi.SecurityDefinitions[securityItemName]; t.Type { + case "basic": + user, pass, containBasic := ctx.Request.BasicAuth() + if containBasic { + if models.Login(user, pass) { + //TODO: Decide behaviour with multiple security + return + } + + ctx.Output.SetStatus(403) + ctx.Output.Body([]byte("")) //We must create some kind of output to force beego abort the request + return + + } + + ctx.Output.Header("WWW-Authenticate", `Basic realm="Restricted"`) + ctx.Output.SetStatus(401) + ctx.Output.Body([]byte("")) //We must create some kind of output to force beego abort the request + return + + case "apiKey": + var jwtTokenString string + + switch t.In { + case "header": + jwtTokenString = ctx.Request.Header.Get(t.Name) + case "query": + jwtTokenString = ctx.Request.URL.Query().Get(t.Name) + default: + logs.Warn("BUG: API auth with token of type " + t.In + " not implemented.") + setStatus403(ctx) + return + } + + if jwtTokenString != "" { + // We have a token + + _, err := controllers.IsValidToken(jwtTokenString) + if err != nil { + //Bad token + ctx.Output.SetStatus(401) + ctx.Output.Body([]byte(err.Error())) + return + } + + ctx.Input.SetData("jwtAPIToken", jwtTokenString) + return //Go to Controller + } + + // No token at all + logs.Debug("No access token provided") + setStatus403(ctx) + return + default: + ctx.Output.SetStatus(501) + ctx.Output.Body([]byte("Authentication with " + t.Type + " not implemented")) + + return + } + } + } + + if canaryVar { + // If we are here, that means some Security declaration exist and was skipped. + // Must avoid giving access, but should inform about it + + logs.Critical("Probably a BUG related to authentication process") + setStatus403(ctx) + } + } + + beego.InsertFilter("/*", beego.BeforeExec, FilterAuthTags) +} diff --git a/routers/router.go b/routers/router.go new file mode 100644 index 0000000..3d04e6f --- /dev/null +++ b/routers/router.go @@ -0,0 +1,129 @@ +// @APIVersion 1.0.0 +// @Title oc-catalog API +// @Description Backend of the oc-search project +// @Contact opencloud@irt-saintexupery.com +//// @SecurityDefinition jwtAPIToken apiKey Authorization header "API authentication with JWT tokens" + +package routers + +import ( + "encoding/json" + "io/ioutil" + "os" + "strings" + + "cloud.o-forge.io/core/oc-catalog/controllers" + "cloud.o-forge.io/core/oc-catalog/services" + + "github.com/beego/beego/v2/core/logs" + + bee "github.com/beego/bee/v2/generate/swaggergen" + + "github.com/beego/beego/v2/adapter/swagger" + beego "github.com/beego/beego/v2/server/web" + "github.com/beego/beego/v2/server/web/context" +) + +var rootapi swagger.Swagger + +func Init() { + // Remove old swagger comments + err := os.Remove("routers/commentsRouter_controllers.go") + if err != nil { + logs.Warning("Couldn't remove comments file: " + err.Error()) + } + + ns := beego.NewNamespace("/v1", + beego.NSNamespace("/user", + beego.NSInclude( + &controllers.UserController{}, + ), + ), + beego.NSNamespace("/data", + beego.NSInclude( + &controllers.DataController{}, + ), + ), + beego.NSNamespace("/computing", + beego.NSInclude( + &controllers.ComputingController{}, + ), + ), + beego.NSNamespace("/datacenter", + beego.NSInclude( + &controllers.DatacenterController{}, + ), + ), + beego.NSNamespace("/storage", + beego.NSInclude( + &controllers.StorageController{}, + ), + ), + beego.NSNamespace("/search", + beego.NSInclude( + &controllers.SearchController{}, + ), + ), + beego.NSNamespace("/workspace", + beego.NSInclude( + &controllers.WorkspaceController{}, + ), + ), + beego.NSNamespace("/workflow", + beego.NSInclude( + &controllers.WorkflowController{}, + ), + ), + beego.NSNamespace("/schedule", + beego.NSInclude( + &controllers.ScheduleController{}, + ), + ), + ) + + beego.AddNamespace(ns) + + beego.Get("/", func(ctx *context.Context) { + ctx.Output.Body([]byte(services.DC_NAME)) + }) + + // Force regenerate swagger before consuming the data + bee.GenerateDocs(".") + + // Open our jsonFile + swaggerSchemaPath := "swagger/swagger.json" + swaggerFile, err := os.Open(swaggerSchemaPath) + // if we os.Open returns an error then handle it + if err != nil { + logs.Critical("Error opening %v: %v", swaggerSchemaPath, err) + panic(err) + } + + // defer the closing of our jsonFile so that we can parse it later on + defer swaggerFile.Close() + + byteValue, err := ioutil.ReadAll(swaggerFile) + if err != nil { + logs.Critical("Error reading %v: %v", swaggerSchemaPath, err) + panic(err) + } + + json.Unmarshal(byteValue, &rootapi) + + // To simplify match of Paths, we must adapt swagger path representation to beego router + // For example: /path/{myID} will be /path/:myID + for k, v := range rootapi.Paths { + if strings.ContainsAny(k, "{}") { + newKey := strings.Replace(k, "{", ":", -1) + newKey = strings.Replace(newKey, "}", "", -1) + + rootapi.Paths[newKey] = v + delete(rootapi.Paths, k) + + } + } + + // Init some extra stuff + initAuthMiddleware() + initUglyFixes() +} diff --git a/routers/uglyfix.go b/routers/uglyfix.go new file mode 100644 index 0000000..f55d4e9 --- /dev/null +++ b/routers/uglyfix.go @@ -0,0 +1,42 @@ +package routers + +import ( + "strings" + + "github.com/beego/beego/v2/server/web/filter/cors" + + "github.com/beego/beego/v2/core/logs" + beego "github.com/beego/beego/v2/server/web" + "github.com/beego/beego/v2/server/web/context" +) + +//TODO: This file should never exist. Solve the bugs and avoid using this +// [ id id2 id3 ] +// id,id2,id3 +// Some Beego bugs? that we want to fix +func initUglyFixes() { + var FixParameterArrays = func(ctx *context.Context) { + + for k, v := range ctx.Input.Params() { + if strings.HasPrefix(v, "[") && strings.HasSuffix(v, "]") { + logs.Warn("BUGFIX: Fixing array interpretation. Should not be done like this") + newParam := strings.NewReplacer("[", "", " ", ",", "]", "").Replace(v) + ctx.Input.SetParam(k, newParam) + } + } + + } + beego.InsertFilter("/*/multi/:IDs", beego.BeforeExec, FixParameterArrays, beego.WithReturnOnOutput(true)) + + // FIXME: We should define CORS properly (maybe more permissive in dev mode?) + beego.InsertFilter("*", beego.BeforeStatic, cors.Allow(&cors.Options{ + AllowAllOrigins: true, + AllowMethods: []string{ + "HEAD", + "GET", + "POST", + "PUT", + "DELETE", + }, + })) +} diff --git a/scripts/demo.json b/scripts/demo.json new file mode 100644 index 0000000..e41ecca --- /dev/null +++ b/scripts/demo.json @@ -0,0 +1,322 @@ + [ + { + "api": "/v1/data/", + "content": [ + { + "name": "Mundi Sentienl 3 SRAL Images", + "short_description": "Mundi Sentinels 3 SAR Altiemter image", + "logo": "./local_imgs/Mundi Sentienl 3 SRAL Images.png", + "description": "A very long description of what this data is", + "example": "string", + "ftype": "string", + "location": "string", + "type": "data" + }, + { + "name": "Mundi Sentienl 3 OLCI Images", + "short_description": "Mundi Sentinels 3 Ocean and land color Altiemter image", + "logo": "./local_imgs/Mundi Sentienl 3 OLCI Images.png", + "description": "A very long description of what this data is", + "example": "string", + "ftype": "string", + "location": "string", + "type": "data" + }, + { + "name": "Meteo-France forecasts", + "short_description": "Meteo France weather forecasts", + "logo": "./local_imgs/Meteo-France forecasts.png", + "description": "A very long description of what this data is", + "example": "string", + "ftype": "string", + "location": "string", + "type": "data" + }, + { + "name": "Meteo-France wind archive", + "short_description": "Meteo France wind archive", + "logo": "./local_imgs/Meteo-France wind archive.png", + "description": "A very long description of what this data is", + "example": "string", + "ftype": "string", + "location": "string", + "type": "data" + } + ] + }, + { + "api": "/v1/computing/", + "content": [ + { + "name": "SAR High points", + "short_description": "SAR Altimeter High points extraction Software", + "logo": "./local_imgs/SAR High points.png", + "description": "A very long description of what this data is", + "type": "computing", + "owner": "IRT", + "price": 300, + "license": "GPLv2", + "execution_requirements": { + "cpus": 1, + "ram": 1024, + "storage": 300, + "gpus": 1, + "disk_io": "30 MB/s", + "parallel": true, + "scaling_model": 2 + }, + "inputs": [], + "outputs": [] + }, + { + "name": "Flammable vegetation slicer", + "short_description": "Analyze land cover and define optimum vegetation slices to prevent fire propagation", + "logo": "./local_imgs/Flammable vegetation slicer.png", + "description": "A very long description of what this data is", + "type": "computing", + "execution_requirements": { + "cpus": 3, + "ram": 4096, + "storage": 30000, + "disk_io": "30 MB/s", + "parallel": true, + "scaling_model": 2 + }, + "owner": "Gob.fr", + "price": 330, + "license": "Copyright", + "inputs": [], + "outputs": [] + }, + { + "name": "Long term fire risk mitigation planner", + "short_description": "Long term fire risk mitigation planner : provides list of actions to be performed to mitigate fire propagation", + "logo": "./local_imgs/Long term fire risk mitigation planner.png", + "description": "A very long description of what this data is", + "type": "computing", + "execution_requirements": { + "cpus": 2, + "ram": 1024, + "disk_io": "30 MB/s", + "parallel": false, + "scaling_model": 2 + }, + "owner": "Gob.fr", + "price": 30, + "license": "GPLv3", + "inputs": [], + "outputs": [] + }, + { + "name": "Fire propagation simulator", + "short_description": "Fire propagation simulator", + "logo": "./local_imgs/Fire propagation simulator.png", + "description": "A very long description of what this data is", + "type": "computing", + "execution_requirements": { + "cpus": 4, + "ram": 8192, + "storage": 30000, + "gpus": 1, + "disk_io": "30 MB/s", + "parallel": true, + "scaling_model": 2 + }, + "owner": "Gob.fr", + "price": 39, + "license": "GPLv3", + "inputs": [], + "outputs": [] + }, + { + "name": "Environment builder", + "short_description": "build simulated environment from real environmental data and fire mitigation rules ", + "logo": "./local_imgs/Environment builder.png", + "description": "A very long description of what this data is", + "type": "computing", + "execution_requirements": { + "cpus": 1, + "ram": 2049, + "storage": 500, + "gpus": 4, + "disk_io": "30 MB/s", + "parallel": true, + "scaling_model": 2 + }, + "owner": "Gob.fr", + "price": 39, + "license": "GPLv3", + "inputs": [], + "outputs": [] + } + ] + }, + { + "api": "/v1/storage/", + "content": [ + { + "name": "IRT risk database", + "short_description": "IRT Database instance", + "logo": "./local_imgs/IRT risk database.png", + "description": "A very long description of what this data is", + "type": "database", + "DCacronym": "DC_myDC", + "size": 4000, + "encryption": false, + "redundancy": "RAID5", + "throughput": "r:200,w:150", + "bookingPrice": 60, + "inputs": [], + "outputs": [] + }, + { + "name": "IRT local file storage", + "short_description": "S3 compliant IRT file storage", + "logo": "./local_imgs/IRT local file storage.png", + "description": "A very long description of what this data is", + "type": "storage", + "DCacronym": "DC_myDC", + "size": 40000, + "encryption": false, + "redundancy": "RAID5S", + "throughput": "r:300,w:350", + "bookingPrice": 90, + "inputs": [], + "outputs": [] + } + ] + }, + { + "api": "/v1/datacenter/", + "content": [ + { + "name": "Mundi datacenter", + "acronym": "DC_myDC", + "hosts": [ + "localhost:49618", + "oc-catalog:49618" + ], + "short_description": "Mundi Opencloud Instance", + "logo": "./local_imgs/Mundi datacenter.png", + "description": "A very long description of what this data is", + "type": "datacenter", + "bookingPrice": 650, + "owner": "IRT", + "cpu": { + "cores": 8, + "architecture": "x86", + "shared": false + }, + "ram": { + "size": 16384, + "ecc": false + }, + "gpu": [ + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + } + ] + }, + { + "name": "CNES datacenter", + "acronym": "DC_superDC1", + "hosts": [ + "localhost:49619", + "dc1:49618" + ], + "short_description": "CNES Opencloud Instance", + "logo": "./local_imgs/CNES datacenter.png", + "description": "A very long description of what this data is", + "type": "datacenter", + "bookingPrice": 650, + "owner": "IRT", + "cpu": { + "cores": 32, + "architecture": "x86", + "shared": false + }, + "ram": { + "size": 100000, + "ecc": false + }, + "gpu": [] + }, + { + "name": "Meteo France datacenter", + "acronym": "DC_superDC2", + "hosts": [ + "localhost:49620", + "dc2:49618" + ], + "short_description": "Meteo France Opencloud Instance", + "logo": "./local_imgs/Meteo France datacenter.png", + "description": "A very long description of what this data is", + "type": "datacenter", + "bookingPrice": 650, + "owner": "Meteo France", + "cpu": { + "cores": 16, + "architecture": "x86", + "shared": false + }, + "ram": { + "size": 32786, + "ecc": false + }, + "gpu": [ + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + }, + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + }, + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + }, + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + }, + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + }, + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + }, + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + }, + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + } + ] + } + ] + } + ] \ No newline at end of file diff --git a/scripts/demo_dc2.json b/scripts/demo_dc2.json new file mode 100644 index 0000000..3d3530c --- /dev/null +++ b/scripts/demo_dc2.json @@ -0,0 +1,135 @@ +[ + { + "api": "/v1/datacenter/", + "content": [ + { + "name": "Mundi datacenter", + "acronym": "DC_myDC", + "hosts": [ + "localhost:49618", + "oc-catalog:49618" + ], + "short_description": "Mundi Opencloud Instance", + "logo": "./local_imgs/Mundi datacenter.png", + "description": "A very long description of what this data is", + "type": "datacenter", + "bookingPrice": 650, + "owner": "IRT", + "cpu": { + "cores": 8, + "architecture": "x86", + "shared": false + }, + "ram": { + "size": 16384, + "ecc": false + }, + "gpu": [ + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + } + ] + }, + { + "name": "CNES datacenter", + "acronym": "DC_superDC1", + "hosts": [ + "localhost:49619", + "dc1:49618" + ], + "short_description": "CNES Opencloud Instance", + "logo": "./local_imgs/CNES datacenter.png", + "description": "A very long description of what this data is", + "type": "datacenter", + "bookingPrice": 650, + "owner": "IRT", + "cpu": { + "cores": 32, + "architecture": "x86", + "shared": false + }, + "ram": { + "size": 100000, + "ecc": false + }, + "gpu": [] + }, + { + "name": "Meteo France datacenter", + "acronym": "DC_superDC2", + "hosts": [ + "localhost:49620", + "dc2:49618" + ], + "short_description": "Meteo France Opencloud Instance", + "logo": "./local_imgs/Meteo France datacenter.png", + "description": "A very long description of what this data is", + "type": "datacenter", + "bookingPrice": 650, + "owner": "Meteo France", + "cpu": { + "cores": 16, + "architecture": "x86", + "shared": false + }, + "ram": { + "size": 32786, + "ecc": false + }, + "gpu": [ + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + }, + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + }, + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + }, + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + }, + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + }, + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + }, + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + }, + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + } + ] + } + ] + } +] \ No newline at end of file diff --git a/scripts/demo_dc3.json b/scripts/demo_dc3.json new file mode 100644 index 0000000..3d3530c --- /dev/null +++ b/scripts/demo_dc3.json @@ -0,0 +1,135 @@ +[ + { + "api": "/v1/datacenter/", + "content": [ + { + "name": "Mundi datacenter", + "acronym": "DC_myDC", + "hosts": [ + "localhost:49618", + "oc-catalog:49618" + ], + "short_description": "Mundi Opencloud Instance", + "logo": "./local_imgs/Mundi datacenter.png", + "description": "A very long description of what this data is", + "type": "datacenter", + "bookingPrice": 650, + "owner": "IRT", + "cpu": { + "cores": 8, + "architecture": "x86", + "shared": false + }, + "ram": { + "size": 16384, + "ecc": false + }, + "gpu": [ + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + } + ] + }, + { + "name": "CNES datacenter", + "acronym": "DC_superDC1", + "hosts": [ + "localhost:49619", + "dc1:49618" + ], + "short_description": "CNES Opencloud Instance", + "logo": "./local_imgs/CNES datacenter.png", + "description": "A very long description of what this data is", + "type": "datacenter", + "bookingPrice": 650, + "owner": "IRT", + "cpu": { + "cores": 32, + "architecture": "x86", + "shared": false + }, + "ram": { + "size": 100000, + "ecc": false + }, + "gpu": [] + }, + { + "name": "Meteo France datacenter", + "acronym": "DC_superDC2", + "hosts": [ + "localhost:49620", + "dc2:49618" + ], + "short_description": "Meteo France Opencloud Instance", + "logo": "./local_imgs/Meteo France datacenter.png", + "description": "A very long description of what this data is", + "type": "datacenter", + "bookingPrice": 650, + "owner": "Meteo France", + "cpu": { + "cores": 16, + "architecture": "x86", + "shared": false + }, + "ram": { + "size": 32786, + "ecc": false + }, + "gpu": [ + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + }, + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + }, + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + }, + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + }, + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + }, + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + }, + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + }, + { + "cuda_cores": 10496, + "model": "RTX 3090 FE", + "memory": 24000, + "tensor_cores": 328 + } + ] + } + ] + } +] \ No newline at end of file diff --git a/scripts/generate_selfapi.sh b/scripts/generate_selfapi.sh new file mode 100755 index 0000000..ee1fbae --- /dev/null +++ b/scripts/generate_selfapi.sh @@ -0,0 +1,15 @@ +#!/bin/env bash +set -xe # Don't continue if first command fail + +docker run \ + --rm \ + --user=$UID \ + --network=host \ + -v ${PWD}:/local \ + swaggerapi/swagger-codegen-cli:2.4.18 \ + generate -i /local/swagger/swagger.json -l go -o /local/selfapi_bak + +if [ -d selfapi_bak ]; then + rm -fr selfapi + mv selfapi_bak selfapi +fi \ No newline at end of file diff --git a/scripts/local_imgs/CNES datacenter.png b/scripts/local_imgs/CNES datacenter.png new file mode 100644 index 0000000000000000000000000000000000000000..fd1ae8e92b2909a7d1d050057ed9cb52aa28e354 GIT binary patch literal 15589 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 Z{uhQ^2E`R)FVg@3002ovPDHLkV1mT67r6ic literal 0 HcmV?d00001 diff --git a/scripts/local_imgs/Fire propagation simulator.png b/scripts/local_imgs/Fire propagation simulator.png new file mode 100644 index 0000000000000000000000000000000000000000..ce612dce049c94c9b570110966ae7012475bfa65 GIT binary patch literal 14744 zcmZ`=RZtyGn7z2W2Djjl;O_435Zv9}Erj5Dad(2dT_8Ykce}W|`||JOR&7=HOwBw@ zb@%7zI}@d>D20MZfCvBpP-LXVRX^`R|4n$<&uhP+@hAWQ5o#?arYs{SM&{(=Xkl$< z4gk<5_$LTR_X`sZ7|y2tQYShk+m6rY)j`KOD$}494OeDpJT`JU!eeKlqGOH?vF+`R z8>k44Zuo=xCxq+~VtaqT=#b*;`!~AApI6{k(u;F}^9=~mhCy})c2ui&dy82*7DH_e z${KhPVQ85!bBiwxiOInikL^-HHJ%7Ldx7VZ)1|mfu+ZY$Uo6r&uXwL+OYEICXkzPr zRbl30#R1(!wx#|skn}h+cQ9U#r2|JNFou2(xZp7ujM0}!@=FkDab#bIcMj1XFiq%I zPpmxbe*2AkxA*nWERiGul6Dw02I6s90W2YcuDdFunYGZ~4ynr1w}8JX>9ZW=d3skvU;ROg4i4XQ|@XkvPWe7BjOK9Ea`y6jYF#|%Dh0r z*4A^Y5fZ01C}hDe^>iJo@g>Ui5EquW3NS`s+(UyqAKQzaTZn%uT0lZD7#*KU?LK3& z!?)e{HJZbuSGrB<>1Nh zIZ58#eZJf^;6Nu}(p9my_4nxi8A46SQhg?!c#_{ndRCxhtT-sPPzG_fO0Krv;NXHV z5L)mPoJOxYEv(iD`#}^`tG}fmHzODSXvQrzMJ_}%IRl7#dWDF+VABI3nFAT7pkL## zuMsFQe|YmV(?gfWL4hBU-=%^ggHZFXpx3rN|8M}n>R@t8%=dB(9*YKa5;zbG%Ii}G z20|cdE6Jr9Dyb(RY{7mFiC^P3ixj#UKjBg@4zm%G+mi?&C3Ipd{7Ui@+uyw)t7&zF z$QCBRQoK)yli~nw(5uVGDE}&I=RobX2I6zwi%@|u8`}FPsaS5SLK;u!{bVGZ6k9T; zts64J=z0-@5Z{}79RcSssao9T+fl8KpZiM-deczk!FkNYKu{vx`)UcbE#CBChzXFS zA_AO`xOsd9x0U6l_Z18v)i| zt`?9zY=5+?Z_{@ECd_ZdQUU)AJ8$e)>l_(tI7!l{k8F$GmT?w($0G%~7X z530pSYF-66g2aEI=zE9I{H=rnP-5mF`!%e2=Ir&CC5^v|wez!>p+kC@eK>(0Rs?Th zqGU@0Cw_)0(LG3f3{7ZV1HSJ;-!9IB4%h+}34ev%1$v-dYUDLP<~_(mUA#gh=NY`t z18E8{dff?Mf=(MfBRU^b@4z~^OrGtqel26*teT_kA7ow>03-1}EeOMZj*;9DWWMwN zJ*ovi;F2!l4P0fhA8xQ~7yQzS+j`FT+PyO!ejIQGIMJkfiU>Uyc(u7svQTU-wmq~7 zb5U$L0z9ECA&##&Atb3@NkM7!VObxI1mJ$)a&m`EL%5@};rT_o+m+)4E5#NVinhEixx0aHY!%-3L4YSV7cj9=}E_(c^B6l=&p5 za#n3X>LmCirpdT?jAK)fXqXt!i7lRy*Ac;qQuOpY#g<&;8gEeFZ8fu8VPcyYd<~Ip z4>1RzsPf+Ox6=wFCFcEG7sM2_-^S0dNz(p1L~TY4rzz^cqlEi(buN+LM4a9(aViD8 zhb90evf2p;>uAcrlU&#}vM@jF)+(?ulm{KIJE6a^U)3Au1Fgn~DA@F?Jv=Xbu9}sF z(UA}&02?exiRmDa5j$-`EkY;MF}DvbYpY|jFQz&I7eTQVvC1h#NG{(gg%X zl~>UCDsj;JH;IB^B+cBRzJGH)J$t%0AiIEw7`&}TDgV_S#7ifTD#t|ha%#8^(b@J_ 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 z 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 S%dP+cKWTA!v1$>6!2bgbuquZD literal 0 HcmV?d00001 diff --git a/scripts/local_imgs/IRT local file storage.png b/scripts/local_imgs/IRT local file storage.png new file mode 100644 index 0000000000000000000000000000000000000000..fd49a1ad4861b0e193c529c5d59519cbd2b25aa0 GIT binary patch literal 47625 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%0AiIEw7`&}TDgV_S#7ifTD#t|ha%#8^(b@J_ zM9>%THCfc2x$T4SeFIQ7`tUm(J(obF3n5QnbBQiyxPohuT4LHl9a;vkd(E%Z217nE z3JmU{3hw!xfrHFl=AHskKK_p-tdHR;d(-gDWn7YqU$S1ZyMdd3VV`=MS6@}uo;5nq z>Cz8@mKmWJfpjNEZ1DmePDQdPH#ROM7nGA za9KV^`?;s(Wa7F+o=PqLX(J1*;Kh=(7?byxhD8&<8HSG9O81Tcoz!%$m;g zQCLxgpssX6>*^t^M`1b*VqdAx#cGB5f61j<%y~jc7Ur!XwM%YJM7X)=caVM4SOBR0 zG;vQ!kOA3CK1t5w=(lbV_*2>_KyKxz_Q5DJ@Zu&qscdYnHYg~zh8cjfnEfpAFqD`y z+rDTXr#EMMDRI_Q%g?ROD+Zq7oAmGuV9DmQrACp=qjoOqmur1GPp#elGf^8_h z5`@=G#3vqbc0?YRaxP1QzRV!@$s4w!6)%fk{7kK7c@rxJ zOz9#$0aSl`szMf4D!wunTQ)mgwm~ zM9bwE4_^{4SH(^^NvO2;v&pryOzl_aWCJJ@TOftTU$Z%~SpTFVm$mb1EQp+b!Imct zp4PIFWH)t$3CMo>D3Q-x41KJ}K=|OQC2w>n6j9l0SW#`W6`(;&TGCUc_2*QrVx!m^ zVw*USH-cm~KO45PMXF^ZxGIphex0@sYCqwWy`CwPfFXjp;=L@uK&YWpS={5d$`AF? z_sftQ4c0CZ|3Wj%4CmAiCv~qZVI&Ud?EM)=$hL2V?^!&HalT6k68zjJb-&_1uYj3m zl%z=tchsn}D;Wm34;kIUQg|_;ZAcz`K8^Y=ZEVCDSm|CgQeNWMcL)b^MC4UsfC-wj zaT0YM*1t;}%+TTImN6bgB&uIR51qsSIO);gH zRUp{(HorN*$*Cs-uu>NJ$m)HQkk}B;Ocf22~dJ;S7A@z)r^Qs|{KP+|Cex8Ej>Y6zZ3h5ii^ zT3lz*^|&;OjM|JPi0R?zAAMYsp$wxR=gL?;$*X0I^y3OLAXX_MhW;vdUG0xULDo|1 zyd04dz)Y?p8-k;ko=9Z z{g(_IE4%`!@>foqlS%;(dM|fWGB-mmd$dv1$93UAF~kep${OsQ=uUr{GxAdl(whGU zw#;9SruznV?KFq>qJ$bV#Xf^mK;vGDV#n+FOhsH_PlP1O&&8&s!C6hHWn&ge_`}JV z5(nb0DY2cv^KxC9EL%=}S#}Vw_A+;o%j9A>EJVJ776Pe>JZdG`X?c)BYULv37ByB7 zxfu=a8cF{ld|@3+NCog{dSCJ)sp$?h!Ec&c~nThNR zpgGyKw&caNaY))ZjgvE)EX+Q$eQ(VNMe9khVE0Y2w|o}3Vp2g5=TF!0sB)vvfh|_j zR{&tDl<+Xx^{pm1i?;k*a`|)uR;XsfXmYIRwJS;MK;sZ8U=Zh^Qj$s^?v={6k~c18 z5a+K6XLHpWT}~U6mmju_eheQ)qKs{!TE(SB=LsE4!`JUWnCt}12iySSE<>uUfX8! ztWzB{xBCsg0!FwzUe>G$QUAHQY)UJCkX}!~ECxr?hVvx&cc zkvCEJt^Aa)%E)|BEi<8vJN;>*R8`%Rb~e0+#RLa-Q3fk|RoakJ(JDZK?T9!3>%_SX z4I%*XJdr6$eoMkf&q*CmQSaOcdp5NF&(wOF=(FS>cCAdzIsLV8+N9L^17w^z%#sJ# z%i}5hM0p#JsiqNSdFnGpy)SZmrw*>!yH#It9R>Kte}!|Ztq?W-zMJOT{(LK#2_`;Z zISz|(S?9{?^7=>9)F>|EkGPTibE=s%SD$;gJqukPR1jN7QtiebOw-xYxGK-&zPD{muf7Y6$`8rHv$&@Zc)--&nFZuChV&6AY=z!Wj8>RpQ~{P z34%{RH75a1ajsPadCFamS9nj5n5FO7ML+({a6o@;;G^1nERLM`0TU)Xi_LlQn;aV7 zLtwLeV5t7dl#vvw;0l?+Q*@7eS_B5g2#MJrU8E;-R}E&1JnYt_*{ z(;b2Se_?j?tQuZjD+Jkb2Ar9bKQ4Ck0V39&rNvrX7LuE?#w-^PVvBFZ$JbTAemx+S zPU-_X8z^84=)G_nHML-nMgfj&Gn>dh0)0eo!4PAa`?W?DyE#(CB!2Y2c0dAXcMVPykh>w}*N8Ej1vVWyudJxa0&)He_u_L*MXym}FLjCDUcv`RaB;Mun+?SntAZC>r zP)HqXR#8Wx;U-o_FVCr)B0S|SW2q}gw_7QKzRx2CPEGM{!&t~XuOh$*WOdYsd$Ya$ zEwJ_=m3H3JLGioHR#RcqZqZP&$<*sfNX$-s8xHH+k4a zufXRMUXfOU?{H?BNzyy-ar-m=;b+fj_%??e7UY_`Sa%P_rmO++D?&{O?}*rYGd!8D z^-YDer*pF(tf?BnlxCQ34QVbCTGqRp_O(lv%%NQ zGo<2w4M{WHI2fl&yY^My7OEt)jw*0creJL8%AnS6ZcS+Uu@qr0C;0lO{7`qJt-`S} z->7a4+eJ%-)E%GudIS}r9Pj2{wYHFUFlQ{qJ?ok0Uj|TTXk=$vo4tla0$wv`A!xG{ zR~%k(9XV?yX~ZL72K$u1?CO;{y8#b4mUVdfsF|J9i_s}%Ju778GLF{VU@nz>5ok`r zRi=8I%B%p&|Pkmb{+RWnx6I;M~9_e*ADjW@)3++ zsWka)BjI==*=^CYoczq38mz_*a-RJPu{GH&6SosF7D-ogDcIUkt_&cS<1YB3%pk7OZF zw3o^tu8c%g(W2Y`q^J=hX0egg}%vOl^)wy6{9 zAg!tihqEg7peb9TybH>w3ZdUi5bZ5L6ZSeW*zAjC`CjGpQuu>e2e`eF2D$cL78jjb zdTBPdrL11--y>>S(>KQ%`7Ka-`^LBK1X0Ris`k1}hj7;I5}Mk=1&1E#V4=>*fq0}g zN?M({PAMZ~mGep4Lzd~GJAM0{rc2+;$7L=n0&_L;%$62^EuR8lhGfkK(!^<*HDg@t z`AneiAp|z*I$zQu$l?t!F~+YUGC~8*xvtr9fOF_g7R}0qReDur8m`chz6x20jdJ|P zSeKPLXDndgWcQhG9ad=P%Nmu;tOk%qsiE~i<{ORs(pY-Bq{dcb8>eWiu80po-H8RB z3%<3?$XF9!s#7gv)R3W#ZE;OGBVZ=gXh?J0Q7NBOeyASpdz*&SfvF(wcw_nk zC!&Ip6h=9DQH$Uz+W-ORjJy|IOO}e=*upKXCxXiGGd}d935qr#dTHX6+1Huedn?@) zqSrmJE|_U~;$M+j_@amPw9VeuuTlyiMfPu4?ySLPvoGH^w+7N&QC0b^3y%npQgi-p zo8L^ilUmE=(dW@!4&umUh;nwIS*L5NShs2FZ7Y|^!wn_C1c=gW2zICW02=$&r24Rt zX`o$8i*(#&f9dvwny)U!{+(q{iPmbfU#-_FT~w~nBn&B*d+#GG&NW+zdpW~ z2%VO8F+B)FuWVmUjbTgXME{;Qw2oQf=-_B- zN235iLwp8WsWEvON8v%jHe7uY`KHj(k=#}F$;0KMrTT41Rdt+&Z41qy^Mi?) zk~%oYd$@X_-D9q)|VTQw)2 z2O1?b|D~5uU2C{{*Afa+f{$A8ekFX6n~-(?o}I^qlFMd^!=INY2j;h1xwVHS^xQzD z45Pej7_+U6h{3IpP%}lMff%UaDfE6P zDERz@_xQljQhx+A7JSMdmOm@Y3aPalwtsN5X!F~;x?W#(Yfm)4w^en$-<0R8>bb%} zbp!fcixvAajb(0`f@&XyKJHl2gf{d{T8evE>26!_y4D`DUP8rZwqCe8ML+U@rd)3h ztM|lz{@7KQ$qAsLuIj(^`>zEU3dK@By>?K^Db`Dw6x>RQtq3jDwhoVXrRIc}ib{^7+_7GAA-V=ug`-^v@bp2!DW6$LP*Ym7>wCq>iv%fm zGB}kFwcgEGpfa_M+QOzIF0RXy;DMj%ai_59^k_yp1Cma}BCADvZM$Rf!sKKEUn}sQ z$_BEydrab>`Le;0+32nUw);6$!0#lI4@o}cHcS?gGtvZ+O9))L@XdSGcKzMK@#d+4L^HAM^47eb-Y0o>66h}V?>H{Xi0d{76C9pg8bmEss zf=}=Ya|xxhaIOBTU?bjF1bJrx(oX?iv47qE<_CD~R#PY^98uJL_@Ntrvn_xtiRSq+&@fBNK-%(TLUo%Sx53)>?!P3a^>7Vg?P{^# z58RO1_LwF4qon`{oV7uCq^7`pIV4u)o+>oq3z5^LzB*3CyH8(6cQC1pi|h6!=vRpj zCQl;alaN!RKxuS1ff+Q@2Dq`iU1TzHZ@zvDUOu&alsstvlU< zB!1`LNdqk5YIN3u5}j9fqKB;ZH2Eb$!mvKKz@!z@_??$h#NgpWJL}4_QA=ywRp?2I$cy9v%bpc6i6Imd>jfJ2; z3=)h+)>*_#sUZb*8~qitnL6%1uFb@R*iY25uCylhkxwc!{|@bjsHYPJn&3kVPk zZ!%l67ZpPXZn*mnSZ$Wgtgk=6OSPzo1GD9S!EIt+f~@*MrK4A(X3ffJ{fPVb{OrThI?gKrpm)PFyHU;Djln(?P zfrBf7nL~-;F<(Q`(BsljJpv9j=-g5;i466I!9p!+2NR`DV>i;>4>#+`C~1?foqHh& zYaimtHS1O`WV~%4or%pVij;a`-k)w;4#fhY1*ZY9dz`(@iBciwxSWNMAT>$@raLox z5#ut3=_hfbJ7&RRVg-W#zxy*lg)kz(t^i6UN?S3gXg zGxwrhfPD>VTrNXz0@6k^&_W~z!|1{VRgy>N-^LBFON|(A2*a~LsRF!lJyi`pbt(2c zufV2HCuLx_YOu2TsX+heXkfff+h|E2Wzh`Z>(cbj=iBZAvP(U8|Jb5FP*!<0r;;Nz z6VuBAF&HM;Qxke(dGW}ozR4!&;QnB-z@Q?vrEpSATP5|tPB@f=)&KTm5r-m8VQW_F z0=&@S`*dc|C2zH0Wjn*OWRL6gZoc*GMp~Mk7=00AmC?`3@k z=C0F2hi$Tx9Td~EIeCjJq{assX5(&S%kEZ<4*ygR))zEN=7_3*JF=!GzSw)W>?&e< zygb=iYh|&7)XG}<2V>cEeaQC4|2zgL9YbL;Pt45-iul9ecU;K!xT~S}<+~lpGQGE% zEuHx}vL^**cxbzQ&97RL{(JxcFY7-pfLgiB3ZKc#1t|v>ey2kr4*`?W#huZ4)#NN> z2fC)i0&}`vT1*=bjL$REkF|unz<(32ezF|Go`%1MatXy}p?y*RdLIvnG@ldgZ{JY6 zJZ}#s>SVDJcantTlyW*R>(MmWIaBr$pu|aus7c6Vv<8;BH52jMX+oQJ!ycb2T-`Eo z*!fKNmp9CI8i!{-os3qo;`SN1t7A=ZUyNxo`<|GUxI;?=QTthtsO0VLuCl+4o4!|# zgk_t`iL)M1x;-%H`zKAzwC%0Z&QCdRC@yn$HS!U5se>SV$HnK2PYuaoO+kB zTAv%(fK%!@lAN>QuuYDj-D7t??vF7Gbq(X^T|g;Q)xqsR%<^g8$(x=xiJx)lV<%$F z9f{4WO)wVS*Vn6aMbx`wo1mP4Xoj9A))zj8MG#dYr#pGJTAKW}d+AE!uKl{nU!=)V zAd!e^SL8jXP_@F3#^0P7?7c%xhUr5e27EDJZy>*VtPdA`BvJD>>+Jk;C;8E`RMHmU z_QDp`cNg_7e8FD+HwW?Scc6gf94Z!2dF}b0;;^jRx~hxgytx!IjN>QfdK}gmi>n+S z=ViaGD1VE0{HGvxw`d}N4zAP0TA1uT4Ev zF}q{>y5^xo^T0nBNl7QYO9X85uXg?g;T;(N8BjFPv_rcC4Wgkoe7jdua^+jlRj3yW*2N4rOcpcWmt+i-EuVL%X+r&ez>JRAW4P8DI(^3ie5P)>q)x67 zisOvR0`R7C#=fc*9p!T{U1j44s*^ak1EZ!Buolf+{>UJns!;4QCTD)O$WM5$HV4MQ8I}D)yNAA8x!%pP~J#X>xq;`a#}gXoD*E@z|rg+ksEd6E!WYNVP_!qKW@H zF>N9MhOdS}QGn-fTtGvv-pAU}%A1OGRiF1A#Nz}((h(N1nGr#KNwr;eIM(b{j&*Cw zG-lRDh#~ld`?11SFyr9iJX@0o=Bh#gXgr<^&M*bmWrtgEY|cI;*#s;BJcYCE*<_t=Skn zr4wYR$`D^CFTOnztyG_repUu4p_4j)I^Z8%(>%89LAc7*CT7^599 z))Y1j?TR@%SQyLU`F0>?E3ln`Z|JK8Z6Ph094&@&XyXKC<0$hB$EAETa}mZj7&%aq z0XG7ce4QVVSO!o>w%;zxN0{8z^jsXp62kRnxJ6r|eTI;OJ|T_&$Wbsx2~CTU2edzu zsb~CQ6peY$rUZ=46)yXdUj3dv#+>mYcf;Mg-l)a;t^Iz4FIHR8DZfb0(O*q;w6FWT z$DloIPs12KF0tcI)_iMaR{tZeDef>C1S!aoSNFK-QqNu^y_sNVe4)MM9&u zgA$a0z%-`tzFo&iACEg@mYSBZJsw|6e_fx)xSeNOi<~2&qLnVh6V<6IRz{E6INh4O zsnX>OVUkoO1Rwx{u^#Q1+qnyjLsJMVVnQlfDe|28bHJWR&jPN5GcOf6aj|e}W52Mg z+V4z+#rC0Z6js{G{{9jem@8vY>#~=q5?p{InhXQNaO`Rt*3%4+E&@+0@I_}Sa_yrn zSea)pT~jj~Y%|CxAt_o((iH+Z>lf)W8R!_-eJ%cqtIqd^5)OBAfmZbNsHqql5X9Ru z#aqZ{oK2i7jX*f&yPLSP15gsfS!(+c+?M7@Nc(dd#C~O`L5N$4-%N3is!eHCxTl(# zWT;MmeYMy#%hP7_K5;&_NW)!_CQLZrF^cO0IX!*4KBQ;xv?-*p)A}F@Yc%p0Q*h#& zpF2vAGHagwHGuT9zgFo4#VbU=K9BZMky zc@f$|>aD(5SKLsum&jcxw<>DDZhbw7?l;MbfmPt#2tnRw{HwS)GkV??rWV`3yxBb4 z0ak}|KD1T$FW(+Xn@ycVmCNo3Spc|WcPC!jTOXqOiYA-2OqOEJ-cQL|HoTp%7lSAI z%M;^Uv+OpPCust~TU7&!Fg<#%5AGg&-V*eBabEzR6bnN@0Gg2?A>$OJYK`T&b#&{M zpisy5O|4$4aSP*+0}pYBH;>1xOV;456M^DDxuLvLMX@wga^O@oAF|-ve07$OOT4nO zWuRqNP0eQTV&yi&HpGu5lATmOwC&${5sZ2>vxdu&U&ieOC_*JOP zqWQL8-zT#fb(AMVbD^ zD0sb}P~5nGmcg-9VzLq5q^QMn8mB|JyX6RK1&N6R+nH1a% z{evXWAkitFg@@5_m&3{ID1I^`e22k#lEj5T&x&)w)ndLJV68+aA;(rhM7 ztO6Je2Sj{a6-Zua9Pr0Mv55c4q=hiyvL*Oi5eZVh!Y|5BW<&`$(ox*Wo!um#r@w(+ zV=140 zIYD4{`VIcaE&?*goCJrAm;w zw8{Xhk>>QhY|f3Gq?G`PmZEL$#Mbt~3%Vq0dP_U5{sc%RJdD8Hg6Z7PmIex@0$n4! z&!vyXHbUkd*Xe$9kETD~cMieca-@@}uZ-zhfw4&ulJL6rJ>dB#3u^7gjGO#mI&IQgbe&SoCyhBe-TK~H9t6=yF0ZOix z@xuoGgP>~H?aAB|$Lj%&1&gI7@eGzIEOkNAa|2l|oIlma3Vduls3&s4zSDm$;8dH4 z^pn$>JB$I{Wr^>#Zz0A?%^YCLv(boX`YdfJC0Z@~LYtdAY6kMJ@92bG)ifrlazM*FrZRw0W83HjygnDn) zx%OpOnEfx(ry6>DQj9)nx#9@EOw)b#Z<;`2lWh;wBbN?gM;R=Yl@V#@HCrqGQJ5P> zGXMImMNM<2rfiAt7Y4koXeUT>&|!9GZ&APfi0ex=h{7bzGYyGkOI^6ikhmKIei;LP zqf?1^ph$#Uv$%VaS(^=$r`5E$9J)OZVu2uo3W*L{MYrm9g_|lh>onr{x$eZ;70S8! za>T%YCCa|ZZ>&@Qw+*;(ZoD`18QxI8TS_w7#~}7Cg-bt}XJzOn23FF1!QnqGdQxLo ziARK0hUx6dkJzh#tDYY8^dMzT-wn28?|$)(DMBC(i0s%|h;!s&H<9)jx3sJ?6Yf5*CZxk}>cY9G7iR`X)# zLuNr-mnEiSw~^`(8b>(mw(M6otfTP&RN2f;ZaP%C z;i)_w4_IG+`qD)&ujuXKQTZDN-}q9);Bux!r5-)YiNq)UKv$Zcjt-Z}Tvi$o((1NV z1vq1Jd^@?V37#S9C$V_?`nlZ+Lj0>9)PB$i_0aBqFt;Y~a{5FKVOIif9G+R7e+-MD zo@HD!Fd430ZSA2`)uU-0Wm8vpH9^&jFZ?@-cs*eP3E;>kcaF_{DU#zyg2#cPwZUUE zDgWc^^zXfNfYVb(3NeT;$TORIZkAcIvo9Ldz#idg+|yy$vps;26Lz~`TteJ@{@v^P zN#xw$EH94;nasr+&XKzm=4(5b9;Erhi?raJc)Q`A%Fr$mnBMj2Tyqqsu`-vs7E-Y8y{~y3!Sk!;A1c?A(>>FW`e2u-P@A&poavxwXp=n@4MGTglgAFPt7SMDaSjb7k`?hzxRML7;TV4gIDk>a&P!^p@Af zPtiJFX{~JMf~Pyz@7Z#^SAhUuk50~^U$d^w)Gp7b7d3XS zsVQD~e$O1xBTHMfg+>c6Jd2}=7*o65djaEngnsc_F4Ls^v3e2CQ;%yKo!jM{S>yS| zjS%;aBzy`Qt^2pEQYmqf8$+%{glO!L9pk8dxeL4|P9kx*-gpq(97R-FII(3di(9yDbgp9N<5@;l6~+9t|# z&n;B2a~?hV6(=;{eaS`az;F z^r)72@SwhV=kP54O;?%$DN(%7!fMLIYpL%<1Ag9`8D3!MKK5S@Nm8(hk8a=T4q6bU z3zpMU#9d~D-2Ym4lG7%p$H-N;Ex&`SQJ}Sfs9t88a`s?Us?}VA;|+Vd^`1&+rvi+yGUKV$=|bl62Ar|vyG(t zFSMit9;3nxJT9eHH%#NhK?1gGERz;cJ_57vaT4KXL@u;{qUGV7M9c3^4wIq6l8C<|=p}0dH?dSodM7?Es?c zbh^m%^Em|KSL6PIj@7?A8dG;8(kJIEN{zVbWaN1=X8L69aa`~PD$(ct)b4i#8T8+g zquq2<(7&%UMkzUgMWURfqtd0@OX8rNdQ_);WcrWRe%nJ1oIka63u3)@#o;@tYE_F zf`fjsm~GwB$=f{SpCAh6${KVj@}6Cj?zKklkO?hahAIS3vo>5+s=7?q@rHcXTBXFt z$G4b#6%`?a)Y%(3?A2KO%Zl*xM{}_5zw_B+p>r>m&NjeC8Rz}yVBapp3Th`i9SuUO z4B^FfnDUaNsdf8p?(0@7NDZvH!@Yh3ax(RU1z2p-n#$P*jlqCipXBn6`fJR8HQhG+ z!XDJxiQR%p2w0ssJzCJqDg07<1`j~(R4V+r%cta!Wec6eegis_x;0;@%nUbCcORL@ zR9PP80b4l&!noj>-%HVT8_;`M8dLr0;o*RHNP7(zj4K@YkAB2l0ff67v3 zduVtztq2VzoO*u^E!GedWevUDci zlIp<1%|eH+RC4URW0=8R4x^lKt?QVC(7$b8b5ycBF+&MH)F6g^z|>>4YDzozKcfT^ z`g7I-;L~cq~(*)3CqnTMq7;!_xB1d&q?l@6!u)|`Y{3}GjYgg8fzOi@N~!0Msx%QbOcfb zs~eHbZ%FYo4}^9Rw@ls8ff2~}kWRSxUDjq(YIKPjEyln6h)RcLoCq>@*k{Wg6a4!j zH~+}ImfhJ5kr|8!bE?}DpmscO1=2sk)1OX9{qDEBLB=#wUB++J2N%o#0uiZqKScv2j5L+}WHV4aRRJ zk3v41@1ttQX@&tMh^i6^kaWqNQ%yvGwRl-+I65lTB|^!vO42mvFaQ5|8cO>>dth{J W=vU0?`TUI!AS0nD{zud( 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

ox+Ai#m1^LYqVYHpx<-3;~?^Vrx;mC=e*wwCzYS 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 literal 0 HcmV?d00001 diff --git a/scripts/local_imgs/Meteo-France forecasts.png b/scripts/local_imgs/Meteo-France forecasts.png new file mode 100644 index 0000000000000000000000000000000000000000..aba85e52403298441238a6ef67b298c4444ced24 GIT binary patch literal 26666 zcmeFZby%ItmOi*~cZc8}+}+(>gS)$1aCdhnxCGa*aScu&xVsbFhMaTy-qSO8x_|x5 z^nVwg4PUK#*ITQWRPFDhHW7;Q5(u!kumAu6K}u3o`Te*5?-w-W`_Ddp!!ZDWg3U`+ z(?!|Poyft--pta*l*q-?!Ia3zU8qR)PsRo>gu%b88* z`uS&jh08mIxU4%$UL**=(EZwyZoPF+Uf-^GygWjVo15s`TDY!Uj}C(=N^d_cLa34L zSI?D!g^(pbzme+HI1&8U2QGbvP7%+S^6dxgyY`A#JAQs-xXMONF8;~$jliz;W0t^t z0eTm|!Kc-0ZoR&9CeA)2tQBiA+Tqf}u9wE!ZBSqBotE|S^UAl^W&5b}r{l|uW$nEM z+{>{Rh1Op$TkGVn7d@N4qOaFz9gi2L+_eJxRbCT*W(&qXGy z-)b?ouSTsy3x*GQYDtLBI=52KK)ZdrdhG>vkjkaOXLa{R!8`CL$;F+zooH4#q4d)3 zAClu)OuOiTsFyz@dg2eKf1U_C{rtJ*>lo`=1zyJDL|8~D3M|>d3i`zqd?I%_f85}y z;CR=>|8R1{xF|}bJY0aBg~{C$$&?^KuI^M;8gC>`U7A=SMR)VTX`Fgrr(UooP0>h- zJWbikvSdNgSdMK$+1j>=V;vMbUE8i?{)hJYnhSrKbM?eAPaM2JZ#ZcT?`T<348K$! zj(MhMs+M_{?*j%Vgx7UpCXq-ON*ip=PV{p8c4&#KJy^%VFPWi?xb0~e57leU@h27O-dyL*JREEy51xl2U~t#myKXK*jo$=Omjh&v<+-0rEAw;pWNH{Dqk?yPv zhM$%VrfE==pdU#I`hc{KK&Y{ACjPyIuMPCd`9=HMeBj)Rg}j>cShtsop0L|%Wo&RYG!AkKEHLL= zT7t0s^xck%rSD>Tf~h6owaSOuk+5S7*L#mq^VvlkM=*cioP}k1PaVd)gm(goSOKjo z)ysw$@dr{&RlxZCo=H2nyIN5i0hVSw*QD>ZUQP-x=!RR?>&p zYy{m4BD{x@cV1W2(S-{tTG|w-ylgP|IU^Mbp{9tYDveq3=)$F5ReTI)3Xh2rcJO%( zPq!lDVG1zowX&Q!b6h+*87CWsAXS2MC8(3NpzMS+9+Ix`YU+jJS4$Zugy`fu=S!lk z*EqyY7?iYdEGlVQTY?qMJDe#mo(xB%r{8t#LOA@Tv9L>)Sg88Wn$MFr?<&n6%ZB3v zK4?P6d>2msIWV1sXfbxvbwnfDOim_>E$OGAGwle~={1B2TDfJm?$ViT zkt}E_!}HSv`kjsErQ%Ky$8<_@%;i9*HG>^`*bzV4+h~wnM`DUgLWMLTesN1}K8eME z(td>b!P&f#&(Mog{HHeomg2rokw> z0V?T7C}&+94+Q47uekYsYw$IphyC&}hLWHmXETE6>-FiT$QhPBosFyVK*_!BxY1H4 z-^7m8jxW2Oecug#uoVbLa17Q*+m48%yBg1c6@ut5GX;J1-;m3elWZFfT)ho+jc^lk zDjnr+gG#y0bc%E@cQlPoj-y29t{Qd@X_$le-HeY8Ep^Im&+6oIZMXAAMs| z44z|ReLc-* z*i9%k6qwjtLsK>wAi@jb6it_PU_759gz&mQxIr1#EDC1QNXaTq?3F)3NXpH|83spd zQXeZ5AmKtHI-!IDZ5+|SMO~Uz_lh}R*bvAY>*~L4*yJEsOhVs5+q~SVRCr;?^MS=h zD@@`0@&~P67Q9W?o(CT)oz_KROOLeCL}Z$0xeSdM1pOkUw%$TtQ_Xnt6NovWB}=7P zV$66r$=Gn>{a9{SYw1y%HVcjYMi8#=Y?uLFy3|C;!xzN3WQmNy224Z~5Fpjzr4Ie} zd?sh^31PVxbd;CaRaxd9Zn3>)Pvu-aEU1u~q7dcR#&&G%q}hY4Gz&|ORg!Kh@0j&m$(VY^9_9E;RVFo@-m*X< zN13HT$t_NbmCN!{lN#@feh@-kRbVqPjD-)rJ92lXdQhhvE)TT-lM++wn#u4XxGiNr zy5uWvHo3cg&{!P%iguV;UJuO6s6Y9zEn#KMOgy{qbbRh*A{6#r1=)rOthI=qj7?4n z@?<%Zo~DVxvG}SF;Vox75=%r=@Xh)TRpK;$wQnsteozRnzRNRlEx1|N`y7>N^1ZL(ht z`f&&p$`=+2b5o%2!o})mOWSC?V2=N0v$np*gB2i&I?CBPT4k4g`K9FrIVZrN_fF;4}Pc~X@1VLEO36M3KjllyJ8`3b#2{N}=!+P4r0BOly)h`_tp76Q!K4gFFEK<4NGTA65N|-mQx;`(IhwxotAwz3ne~P)LL_XRL zB6Lz=VQ_4ruA*Q0wT1&9aFY7$f#a~yky=1Z@kK~D0q-hXM0;Pb0Q{>4VoDg;`L`fm z!6P)fkgr5`fnM$jV!|z~N7Ik6LrlCsjgS)?S&v5bZ!2<#KV&q_d!boJRgS!puMq#x zCx^5bjD7mrCGmskwo(&guh(v*K`GRO?8_K!Hm-jw7g1M4AjJ(sDtRAoU3IJC_XC#g zeDEeNMGNK4(jt;u?D!IqH;;{&eh7FiR&c=iaU|S2HfqBG9OzIFLr{0c4z7d3eV?eJ zC@KTKs2jKlGm$G(@^r6eJpX`x(e~0KTG$*%Yp8xDL%b<_Fx48xVB#`nMS*?C_s$cUBovkjA%6UID_c8k2H=d;+V2k_Muj# zNQO>77LYGUoKLn+ON3+TXjvUJM9(PndI>J$!(jR#0b0!R3USvE#F*HuP(-GpbXzs| zaEmO7T95E%r6fO6!Ou#S&c}8DL>c9y>#a9~kDP#YKPF@`p}eMCsqzBGVu!(T1r=}_PEg{d&W6=39A%c1G%pvU0nDC~5 z3T^)ZmXMk6RXpHv(M!lCQZ@Y5D7ZnW6D3Eseg1G@*}QsaJAj@4QQWej6fw42h18i@1!pYb@ne7E#b@L^ z66ccKB>FbRMMjwCS=TKj)ExpD20EsM&^yMWf|qO|S+hkOBoyUmkvJShII00Bw9Y4QJD@{czGPC^m|+}{CWUeCjP!?DaD?H*MU-xtOE{96b_B1K9irMV+#+G9 zQ(v@GmD<#wEn&|cpt93CnT)7EDZ>?q)g!VAtK%3UavgP50n$L`K)ulA^a|5L!0y61 zdg)0bpxJCDZ9rdznWIoa>7@lIHYM=`v0#2Q>*`dhG>Nt-$Re&YaBU0EIA~HDLayLF+1-s9)g2a{gOm!rAE!bM!lMVpDpkkai4+;kj!qd? z$H`jDUcxOS`i1uTVW9Bhmp_8Ai9D}PbpcmCrg?8-Kn;3Ah*JP*H2ybG>OnO@AlGgr zbCqO$_A<18)Esxu68nw0IvbU!#aAKI9lS)}9mISC_Ov;e+TX_=R=8c&^1Ai=o#GBMSO)n29s z$ER~!(fQg*6+;9+R07*qLVwgOY;vdi=}Sev!iPiW2r4!AySyk3aMMLfQ#43<$Xmwy zdnw^Tdriu};&k7Vdo)gB^D(1Mzf0o9 zm^P5SF{A4IiUg(o0zu$&_P~sL5_!Geo~VjBiUma zl;TgTb^OCHQVOkvXIVwEIeUF$zAn~#fEUsE?H0xqi?KmIFg_Yd9ldU3!^7Dm#BJ2D zoS!vU2ng5Cz8PNPPG#1FCLarY^xQ)7Xxvk_pz!8Jn-xI`N-xHrN+Y-qUW!UagiGOF zQsKjib|*)*wD|(vMr(IjK1?c@ zh7gW1JR20Tifb7w(xmK7Mk^GE*4)Q&U#>6!?6ti|LkBeC7WYmW3yRMDjL1hool}uC z+wjR}L4{Z?2n2f-V&(+X*ZNfZ=}FY^3P*#sXq5cd2vUkp6tovM0(V6bbjxVU$&@tk z{rSn{Vxm&eD8SuEeGlD2y%^ft%N1EYFk4V7dGe zNfV5kABTj~5W;@}`H8p)0V{C3l{BVSg_+L2Ei4u9P!5r~zHd1PTbJFF(??Qtd+fA8 zf_Fq~Lm0lpyk8s@DL*7)7>W5gyxp}E%nGVl-q;sdtKA1ud>X-(dRmPIXJ(3fs23S+3(}bO6gjwKUHXChv@x|&d4MrN z_mG{M!$Qpy3tVnByRmYb-x**LKL>_^0N4%OB$x^ecGtENuXXrTy%YkP?R2N{BVcD+TNm%IKxd zAO@FYWSc^#=~wL4Xpp=_RHpOS#)$w+#!vV>82haa+=1$T48bzK1gD4Bmt+tw3%TzY9^;I%YS_-sxBg#RlnE z^sf-mO198U*%G|sqflocpERRFEh_68KcXCtY@xXeB5Wgl59I5Yo1v)+;Ez9 zCTY~fPG+*G$(hQ*W1Sb-Ps(3+Tn*ixMQ)TD8CnEP(#2;~P{L~pjeDygb8}vO+XMqg z6De36E)$o=S`iK0NPwgjAkApsF4^sD=@i7ixcbHcu4a&tu0ExKO|dV>ST6)6i1=tV;W8LgA&U&0rkNZ1wW ziXutkjV~{U2`eB8MQX&Ii!GYvogM<)8b(Pkf^Uk?&=G;igHBiZi6k&4614G!^7!aC z7m6~k>DoxqM~eA^zLJ`dz1|dyv@^vnv=rK>{u1^oqTD6*D8EXKQ<$LoQFs}PMr~NY zPLQy0u%*ZIn&BU8u$=s<*rZEy%h|F*gGUvDLOfWm_-mQek7oi8aC8c_u!3) zEO6v<8r3#4PDHk<7~yA=4Si`B&bLTllp)UglB+V->X|rFzCz>|C8Ak4yuj(t=OnwO+op2Hg{=)p!QNl z87#sKJ|K^m2yIXi!D(15=_N{q+g@>Pv)Kk7qRd4ELKeC+1riKls$vqI2u{K<>0BYP zV9=>Y(P+b_)k|uFY%S&Nwv$cj2pd&m$Mt2%0?N+Q28{|{wZ44@-hl|iHBRO}5>fop zG7HyubBRsi)pFnwT-gm>(eUd~yVJsqnD$j@mUMAVW3XO`N&dlUQuZX9yJnY5!)Q zQ>_PsrGma8e5!Pe*P7!3+(+-K3v%)b=A>(Ajl5ke8*A-rFivI?`cCCOwWD+kv8#MO zM;aX48spn>5s7w@sp+aB^*DJ%|8grLOkY%~*+h_{f|yVZufr+24JnUvkgO1qU1g!N5PeN>fNu$Q8heUxOESukMcW~BXY;Z!K}VSg(*22j z^wt)omhkp22=u+QigGdzW8Om*D1-Z^A0hGORB8YUATo-sM(t9xndBOHEh`%|OtsO1=O0bt6# zyXH$!&}+GX?zvu!4DZ5|TxGgMRdjdzn!?k{SyPEmBaZbNM|+=j(efIfDQA4}TtbQD zu!{q7%xX~kT}gk%fpQU$Y2bv;#i*T|0jO~5_7zxN0rv?k9DJDQsZgFVe6Pi#sxK6aZ;Ra zvK)xK8QwXCqQvwDWjO~pfkR-u4oCuAoQ9I+aXM{tKg=#apKJ4;LG1imN6#dcY8V8%T|5qm_{k8An@<2yI1Ms}OYr z^q83I?BI+eHKWe*vY-pLh5iBTD9eHHGx6TM*q*R#DOgR*qi`a_C%c(Vl)1ct6dqgY z9cYY^oy5p|oTe3KD{!auBkA(D06{}5c2&amRxOzVG$@fSTbCh+VFE=z|4e0T6!K+A zQYTJH2hb&@jt2tAEaZ-C?2n@r43@5Lo%(*0s(kAHb(T=ctg*+3rTIbQD3HSNqM+{N zMAHR6_dKSe_+i@2Bd=MVGh$OV9$&U58_?{BXjd-MB=sMEaAMzD+kv#p!SVv39N4ce6 z9PJDnfZYT59S@ncgy!JTw0zcKo<$@v8m5?)Q^3^rwiR-U^)?37nKAQhhzpX>A!d#E zh`GX}0l+sFsv{)J51&h|-GJCAa6O+s9j7jgwwQLGNvU+DQLB;;ladr)V*qRc>Z$H* zXb;88i130}p4by^vFu5@YMJx!z(9QP_YWbjRdotpym*C8j! zZg9A}WT5y7Sqn0rcLvS8+YgGA+nrW|5@%2iDa5bqNhvDp+DKF5YA~?HF2($g#z!I| z>*-)cBJwnL>+zFE%3P4{SA5zv+l%Ped9is&nxA=!PLIkPO76rNEwtqf=SG#F_ItxJ zKgFpequp+EK`5F2#Fmla4&kfF;LeR~TiDChpaGXA(6Z$|9Ub`&{H*H@Q{-XmPgz-H zlJ(Y;8X1MxzbMawyn1(Uv4ARz@Ah-Y)~M!+y~kQX`!o&)v6QR|AUN%~X}9LCL)Mg8 z5fL2R4x>>6pinX=0kU4e%hkG4^2NhLae#Af?z{AnU?aPO8)h}#p>{$AWbqV@zL8;-)r@G%%hHSWW_c)~<5epG2mO_&=;_~bA8J=8)w8`)wS zR@^l8Cr&99uMHF72nOA^9UO;3-0AzQBUGs}(v3Nu zNUHvj$zg`47lho1TDc8hN{-9y?HKc|+E4)e{NdTbLR!_!!4sB#tJRoQ&@_qBv*|a( zEdrnAzvrX*%ICf{PSYq?s6{}&+Gye*ozj$v{W1Ye4t5FndK@oUqzJ$KAx`? z=iI!hMN!yxG&Q~1t|x)F1u*V$JNRHkLpAK^u48&2bQe7qs@i%KD&iO+E2JHe4B7jY z<7YrZ6?85r62fJ?GcV|_&6tx=Bz zW_6V>g^CTgw+_AmI&Fn|tX^qv?3t1vuV5`M)2Lr@3F4jb5K+Hm0G(?vJGE9zKpoYR zSI}-b5Bl=phU==TKYnPNdVqK8#1e*Vyu2wx&IcnKoXamFId1i@29Foq(V~1{hD|tO z%YkY}0Hal-0$>FgF!A)B!kfolW5EbUS?t)LRme^wi~G$$AoauZkzh5itHvERL6H$P z^CFgg4z+E?h}vp;KoNX61XqXv6T21nEPpt|GgE$?`tr9|m|`vj}CZ`lc)q96G5m=0F974hYa> zdnc?~w_sekvByJ%zVpdqnSEdSD3!^zk(`h#W?KYDj%uXWE7C#E4O!%-`fXp0NMS=| z(X_0f<cw#<323&lV*r!I^s&o;JbTy+-9k$MBr*#y$TS1?17p|)Jc>&@=~ z2(m@#3g@4VkvxhiB-EHA7PeVr$E0*<35d_f3Ynu-XP|#UDPNh^r69sEo-m7bFYEol z_al!7ikf_|o+Sqftk=9Jp^9QI$3>uOjxQe)&r zPOwp$H+UdiS;J!Lk)6yMQiI<>`oing}lC|6LS@1Y5UH?fZ%Vb&${(4 zBnidu7&NSj(6Q1SQiKIEX5QxZ>R7b2 z{7cAbc4D`~WpM|zO8kz*_K_KHTu?Isd3Tnb3Su}`DGvy7cuh?7#{8PFc@o)DYxL6j zN+1$EvS4Q@A{Pzgr)s$}Cbw0_kRTHyycJr(d&upD8HCW;4TR>vT&tduqh{Jv6kz&N z${tBOrHF1?sOj2TPo@N=vE#{7%pg57N0S|V>yDOuZO|qGby8DMB_HB|3(0O8cLZF0 zsLRI77kZ%)Mmgns#SJC7R8v?NGTNr2yL^1tpk4JLytkR1TUU`^txy`+Id0L};y*pg z5$>=bE9P`W&E313&{9jQ2;4-?y?{MbwM-D_l`_R%tBev-)vZ?J~C&cM7 z*F)@FbniO*y&vVhZJ&sA6a`Ov?WUNP9r5hc!-52xR$MknqjH2whyhy^CELwV1 zf=JAic80D-R1T3f@4r3Eaww#QU73aGx{eDgdiN9TE6N$lZr(A4!G1mqP>@=O@L>av zxeFvoE>POtTTntKOJx{q1iawabN~askAA+YX`6eer*Nj`s#pP%ED2c!77{4$Aw`Ht z#7YqaShjH4;hq8I4oG<5?8^)HCN^`KUQ?8VOYI(TlwvJ=bk;_$YF*Gm_YKE%lBWE| zrpv&}bpj23PIsW8Rz&%@n7mfzbj?Xja~`LOBjZI}#EZz#vLT!tX(Uz0)m`7&fThA% z({*a#Jf=61Y7nDH+*MM`-Q8&7NG)lJ=Og#$3ZA!u&h%XIXvEC}kFAqH_RzhXNg;zs z&7lfB`WHozL-m1z3B6f_6aW>?G%&~;UWx|aRauOqn_v}^w{bv5bV|Zv0fMVF%i;iX z^$R$a-3K!5Fl;Xe<^xH0GicDBROGy37z~5}3~J5P50YjlkD~WZZuqp_2wB*&>5m%l zHbLz!Yp-kl&8~ycBx|~F;859n^VT)rFm%fzQ5?GBOVm?mS}wXlc4DvFyYn9xY}@vw zaj2x${MDN8D5myR=e&r6?4GA_gjUu4cX96>Mz; z&xd3+@6-G;^uAA?x_GCVGI`Iy@NCqm*@`su0+weU99->L9Lqq^A*8BeprHZlEKrlVz(=T{B?@DA)gi=Qt86R;7T*xSRmq!vUVi8bG1rf zzaPjyL5l;vQyPKN{(6E1rYX^a?Bgf#{Oont48Pan3P@<~Q&hHG92{`w-Vp^|sqC(K zG3C`+eQxix!}st73ba46xLw~ty)DnTzp8LgxayUgN@>bi9V>D*qxX7|zZydqOO2L3w+L=PI8==xfyeYi{NYN=4 zwF{PnYU;3jW_U6oO!C0|6v+g@1^_^UEJZ{Vr9?#j28N18uMnCyk_=;I4+!K zFgObAQCT4*9*nlDGOdZF!0tAo(+g@Fx|3SZFjE|YMZB)SJ>m1&kB@Tl9#Yfj$bOF5 zaVSLfaE-XJ*S;s5)o4Wb@H&V1Z{lc@eu--b7MG;?pYd8-&n!j>9NNI(`Ely0JCx(U zD1HPvH~%FEVX%RDpnKzGeZKt*WLQZf18-vk8JkY!HgmGWyWRWgPPU_)klf!XT*HTU z1pq)sw|qZ7s3|AQZESB#Z)jp~WJ>R0>+qfy006Imhl8Q9wW$k{k*T?*9UsYgM>h$P zr3oL22AdqCoP&s|g{7pIlc|cAysEL6wK10oi2y$=uLt)#fvu^FA(4lzjh!>M2Or5F zyxi~Czr_qBM1MeBtocYZqIhrd zkyyC6IB+vCxVyX4yR*>SJDD>uadB}mFfubRGt<2z=$t+6Tns(v?3_t|Q~b#xYU*t4 z^q$L>_I50%!5!oLRi-Ts5#*~NrG>b=4GKA(3012ZEd zCmka*9Wxih-}T>n<>daMZRh-#7vKHK;9=;%z(mi;U~BuIG@M<;-2OSuMxJB)a zU4OGkiSm*BHh|m2-q_NF`;SXg4ik1pGZq#)V-|J}I#xCl4mvItW9E0KGnp9~av8I; znz8(iO3KdJ#n8^!^f%QzIlbjOj|nTgDU*pYI~^A@ClehjqlpO}r|Em#%sALM3^@&r zIG7As|3;zUWci-jhBkk<>Nl0iI~4~PD+@D=5f`0_85<`Zt0^NRospRdGo1+wBPRzJ zqmc<0i_srcza7ghq$tHl!c5QjPl=+9p^KTllPw>Ktfif+$3Ht%Ep1IzTnv9ZjftI! zgN2QmlY^O)m6?Tw{huIpQzz&5to_ZNJ?`A3h5F}L_Tqv3n@SlSwzn=&}qng7x8 zZZo%tlc}MLy_2fFy$v79Z<~mItNu|7M7)1xIk&96vElD(_ycTe^1D#}DivWvbA~^f zybS*p`2WGAVqx!Y_kYIoPv}2bgq&R5?VYR@oD__#OpRUsYo31v{s)ut`y%D+;^Zmy z-%RTNhU5J+EhXP|?VUXTHol6fi>*(w=jJ-l7^9)n~{;^&+z^g z?*Efa|KI1s%kX<; z|BrC-{$Bg!Xj{F}t(`$GUoBN+)%z}xTd{I1fZcL|Jxq?R)PfJpoM1@e$}hWsvs za*>h~gW3fLg{Fp2-S_1)vtMl)Q zA`zP*DZm`5rybHGuTSQT#iw-h6WD*wMjPV#ib|PkGJ-?JS+6hcfOxbLE4fvoIl~@h29(7J)BH&EkFSb$AG1d;QlK4 zHV^U)BoSD7t2^X>9H9So{PLRGcPStTXnX6M2Xd=T?Nyo6(mrWMWWKDo5Kzn^Zaf{@p6{X7r9O;;Dqiue5RAsXnM zu^)&mco<;Dg8tUg^;R;A><{w#3;kNheEHY7(_2u32QAXi<8G(iA3NS|o#o&!_9r(2 zJW)*h!hn6|$?R=_IVgu3-M9_hEfkQ>obd;Tg5&wbN4vK{_`BmuBdmFcF5S9QXP;W| z3lKbLYcQkR$3g1rl>DJ3hHkjxs#typkdww1>s*#pK-@C>!njlSz#1Wc3^=sHcER(h zWpv5R{sz1=()eYUJ$P)6S#G7-!Gh^2oUz*0EeqLmo20*UhWUiuMuvaQ%yem{Lbh49 z@Vw@DVH!yLsb+(3>g2}Y1cbIfAxO>-<#iLTYSvc``RNv{)*nkq0>rRs0^qz>gh z&YAl@XoBm>`O~VJM%{6F+q@E|cNJW}aK4Jp8;Lz;08a&z)O5Rzv3NKJ#CY*}VOMPD zr%Bc2{2o9h4;p?e14VAV*-4L+)R4UCFX~=iJyQ7h;ESbeU489&`uJ^cp9Zvwr(&Ev z_V|F^O>f;N=%-h=9ow`Ed$Rj&MC^iHyYjZamsYL4kXAmGE%K&dve|oD;1`{?PdPZz zj8Ls`i_B5;KEOdZd92V?t7QS!5G`e3i@YJ&irF@f%I1RY6Q%s6UAN)#N&R&;?1X79 zoG^e~rURYJ2k{yH9}1^B z%K?7gFB;}V(N7id-}n9P!S0qJkiIm=eTrpfZ@;y*MuT7WpK~`S_9y&#nFsY+%ch-I z7mcFLk)p!b(^KIwa7ULQ0Os?Hcfz>`8W2@iyM#rpne+LacCDvn0+{)R)iADN?s~1F zKJ@g%=<_&aIJa2>L0+?$(;|s4zys!rqb`Q5S*tfk4s^x3$O(r>A}=OR z=QHGS%M`Pq%w=@`oU7WEttd2hxVA; zTbFg7KAxKT$*-AZ>V=ZYp>JFl+iSUJJsFTRDIp9dh$+RT{5NW=zvVPrmP z_ShjgPlR-(Szy=uMEi6bf-z@%CDd*u8#0|={+>_Ea|u1@7`N?hCF@6 zsB1hrC%Ito_HKWxaWUNfVG9HDwpQiie+)VQ>{_IEpF(2yyb82pR)>q74;m-a& zKyObKPf=BxkIW98@38LX_9Ky}w}m&oyz}FZNo*DOT;y-ouxT~N??H_a6UCoSt+6ul zZ2_8pd43t1ABOU%F723lH^uYg?j!mA&QEFoB;fh4ciMHXUyc!@$pU^mOlVhq@5ACe1-2+`J~C*zBIgylGYHHEAZ!V|Ct9k2SQ6H|vF8Gl;awf? zCWVx?)`quwohDoB^NQsOfUOCLVT@-0azXj0muFYq+3lMwr7GS(OWp{Ze?PVT!RBp7 z*O31$&5rru;kwS^jWGnkZvbOpg>%ydB%Cx{d2`5ox^wIuHADsXL5hafaTJ5Waogm^|sJW?{4*1o~ImduEmvKT)R&l*_tOZ<~%js9-Ao= zHQzQz)jq%FjEW7l&A#YaxJ=$9{0t9Jvu3ehc_`4;{d~8O^oX?TbIs?ov;v0A`v_Q@ zW?64a?P@)4IDPXuX#gQ8VA9zl5W?nkG|9g2tCh3rIEdZR;J>kd-Pefhbtu24AmtX4 zHd%DvKwe+^b`rgxTodl<<8cZ{`K`O7_cdyX{`g*Kt;!+xI#D|iIJFha^krtpHCr$L zvFmud-jN>sVz7;b(7-o|==x$isV2tv2JgeC`kko3^d#Z%^X${b2aiC59^TIF7dJ8V zDEGV61f+P~CaZ~UNVvU#WYuO$NkP(J@nPhgSQO|mF{5~`Q&utQa$nt|Ig*61q zp?aOIlYOnO%topyTs$+`d6g-pjxQ%W#yQ$}IV^5qaKNDnjED2(IpNjIAd@fJKjjea z!NAbvbDtBL_Hc;%wIooS$5LlCNdQDQN0muuag)c{*XdApofzv;>?8|gB-D|=j=gY2 z6Wq+x2Q@<(hqv)YaQ7KWDF%>X)W^U+5OY~pA@oVKkz9icGC#H@)ku zW;!w^l`ZoM?Sq65Ooq1Lx!o%^|H+cgV+?w4^`2QZ&T3Yl>~+6S>5Eoy_+7iQu6jaQ zZ8AfeJnoE+X}sZHgd_I~P|7&y?H6Awik6MOEg)Ei;(glVHPgYI7DmMlbHs-t*4##P zkZp=rifM$vX(rP%p0Xbz-!O9L(AreY?rG5`ur|H9c>~Fi6}8d{CJGfA%#YLz&g!#{ z*>0*KYVxA{p7H5`^NO4D_BH-8L`JHZM>AB$im&rQ*tBrYS%C7T2SfHi{qb2nIzWtB z#KtDPKb9{k!}Ktag~Y!Tp&*yVMrTg&KC64=;=I|BvB>ix0*v>zg(o5qT_NY>^ze4_ z{eKKRigV)~I}Q$(b`M83@Oaf8N)poPg`*P($QwnIg_$)u$7jn=ug4cQ@r}%vr(q*0 z7iSoBEf1kkGQpGsMndGmJs`0`%8`}*0mR3YO{=cp$%stB`LNjAfV6^R_Jp6F(SwWF zG&Kj?_g@}PK6^5a&a`o5P~P?N?R5N@T}dt$LE1!;-S}2dxT(?1ThVOx$f4&e4*th> zQ{XH%9vKTR;B|$3$M2K8Z46~@b!E+-?Ku&z|26SfY|d;=oI5=?52(k{Ax>==d-IaV zq3~@>wV~1_c3O#B;7or%_=r#TkVU3ZmmE=}lJ0=3xsAFlE!_7oPbDR9ioc6|tjl>&l?os*upCFDp+g}`XGy|q$)_B?~l@-Y)* z^uiRz!B@Q2?z5J<7$mghhIHg^j+WG3T*lofr7@PCmTG4nJOOjyIoO@QwE3rbcpF&0 z?`hS}@$8-(B?g;;V}RZ-uu$+#Zd3KGXazKI%9>LVm?`LDQ(OXDoin}t9~}$x%RR5C zeOQ7+kwaUv`iT3h!_++kzn?kE#}jdU(QJJZOYe^8$N}wTQXhw#DJK~Q5bu&vE9#$L zZ^}d^eF3Pr@ydF}HTGCIgcR8AI_-6NAUodUXfMb!ULLYSt=nw%gNzJM*Igmzia1^x z)kmc{8BHrEyO>4Ke8=2gyJHV!OGC+NbAQBkLHAzo?J~oyXhc3~I|M5|>3&M(G3#o1 zOQey(4o3uGhO}hOg^9?J48(z98Zx)+%)>a$0sokvtHgf5A$59KBL861>J%&&BOu`g zssnW>?LgM+h#VUpE(j2=^Sc3jZuQECeIPf%o5C%E7q^U7PFFM*4xtxx+Lg}KeXt6R~NQH(Ad-ojwhf5!+-%Qn5M3x>< zN+LYSs_5NTKcQ&39J$64TnGG->#*4CU(+HxuCdwECePqbv=TDw^ef^HS=-PHP}C?h zMAN1syvUGOq}`d_U#H4 z2BgAyAyA4YE-hUs1;K+y`(_a#Qiv*5N_TmGy54b!o&OrmgoCh;>PJbPfCml0IOopS z+s`N+YUAafC}prR@oUfRk&R7N<&zuX1dVCT_V`}!S(Ufm4_13lR-;$){nxao3l>bd z11#-YV;crzG{y3UF>GinCy#~RML;i?DmeJ54gp?@*mi}`afVLzp z?Mw;Hk6SLoO2WiANO+2Vq*U*@wij+mY~0C$-X*O6=!O$GIOYAtwxii@8QO=%V<$l6 zQ)Cool2%=~Cj;*%t)GcvO_NdR?!+pz2Km-f9m4CI;6SS zKP%7`_!VMuVAezy8~x3l?$ zOTEE-zF3~bp3!vd&>KJ7!~&e|yWp_@V7_Z_UtsD82dXHJ7B@7d9TX507S?(<@@o0Q z(MT({K<6I{$be5^O3Oc(z1H^9UjBak!(m9SR<%_hcnp=u`;GESl)n)o9t1mv@nTK} z@4l#8Z&1>(y=*u%u#41{wTXA+0yyMpR%RH9Q(fJcoQH{Yp+URXbegABI(y^ZCAanY zDFmB!c_}hG$P5P?>7kd8hmm4y$%8f)xxR`s*XMOO`o-@tve&*F+?!)E)iqW3u z+g2FdFjmpiG-bMYtnfWF%Ec6m?(?laeICjKd8_X#Ofe;jGcTB$YjbwOjjzi4VyOH6 z{=Ewe`2mE5@YZKAA5Ue6y$S*xG$v{=A=A?-n{iy;`i(Mg-5(kBu=V7CZ{m)i(%~i` zNlaWEj#g(XSQ)m2Z3%mD=-TILGm@;$!xEndNdhKC*n%@%UrxdpKHKQ$d=`wntR#V# z7yc)E-{vF1Pb#*>{uhUY*^}p=jSm}N-_9oZ+fxa?P%{bI(MsX>ZOBvlN2x!Je#qgehpl)Xd)+zEie!h6HUot*OF|9HN8Jb8Rd2lb1c_#Lu>Q6CN$K zpORuhKY#%VbDhD<*Ea8(8?T@=nlHUI8Xd-0^Foyy0O{YOQEZLe!|OMM;pM9$=*<_!7G?4vLt;9_o?_ zl2oW)I+9K%wp|dDtpeDye=mCP>Xy={@7E`HpDW%kvOn8j%)e|u4{+qRh<_tijEt$A z(P=No4?N7)qym=&Y0IAMQ^4PvQVe;*YVl}f9im3}Ek?jPU*!P42K-+E#2P#0N~res z>lP4(ij8Yp;b#@Ku=A}gT-P)}kV`O>om}s_^(uJw4LD@$i~qc*ldCUX&XPrMVy1*Z z28TO2F+LfiS6}jC1@Qum5LJH*#1sNJ_?m-0GJrOtv>TW9DbYXx@9u5Odbf8UIuim& z0S|U<-F9zx9Rb&P`KN z^7&7!K&gPh7kHkBl#(Z3m|Oz6acw)=z{1XY=G8m&k4O2~pS-b*^&4*?-F^#!$q^my zX+?PRBDz0P$N%?@z2$3v|I-`z)eq$fLNUn(Ff$0{#`;4TMLB#t=|KFMf-2(%#`8$> zrxNk;xkUVy0z135ZoA_g@s$I9?F{SQ&l+Memt-P=>rPL8w6)c-dFkYI>+P2{@CU!X zoTerhrBxJt6EK#^v;Dcsmb&+CY9yUbAOP2KxPEgbv(`Hpr;z>xUc)+)O>3vdIoPm% zK9?=8NQNIgm!NO3n_SVEA{w1p!&nuJixRcGtugop;2*lSZu>UT9<#ZYZ@mcoMsH8| zYv&W$m#<9o=?||1Auvi4DotITOFSOOmDB4?vW z8iDihi~VPtg!vTk@vg1gwiEp}-IKjN-KW|;^8)b2-k$DnUkGk*Yl_o#Q72Lew29KF zei)L=<&my4BPXp}-Ad&*C*UW~XL$d0OGqW_NG1~0CF>AK?tA#HNjQY?NmFD(K`*`5xXu0lxeuPx4B82_WA(@PE)heIo_Eikn zr(f>kgE#C$1q#0aK|vDcLQJ862(tK7G+nC2pckbns17o^fdWZ#B*~RA*dO@p4_0s>Op#R{1zc1VgFf$R*v0nqwceiY z&%NFGJV#2T;~+(pXV=;%7V}6ZV>3zs$91`EV|$qZ0DkghKeyeu8qbU2NQe6#+&`(; zt=BG&RLX>4H%POKR8`8=Adq)&TiUz5`|v+`9{zmzlR`S>_`0-u9HXiWf2#Cx6f<14yoHz} z@Kv-dM}`CT>>uRvOO|rr@DR`Jo?OJ(1a>PN{JAHkN(m{GZKL3)#0mahQga{)*!%(c{q-P5U_M{ z3#&Tf?5m0uJpJO)DV(hcgAm7wh_kJwj(_{n6@2jr2c{5!OKa;|z_7MK@RxtIlyBYF zOV4vdr%-{QjX3MoE*%lGj_B7oe-S|U_H=KX?WWz2zje*6>%aaR>*BOFBv`X*9$IUR zF%=1bAeSq$_uxsi(rB$wO0jYM0viF;oE)5Bc(i~DLmVdxtSn#D%;96Bkwmd8+|iz< zqqQD`L2HeWE-DOBDoPC>?HePLDWZ)=tLQnDeI9|JwWW?losGDzi|2Z{o=Ywta_~sx zVj3Og{*y_MmCM?yq?;jBn!`s1*u8I%p^*ZEqeU809`Tr@wJA>Lyc7-fF%BJz%HhXz z6YM?F$1olPZiqey~+sV5L|eB)6tNg5w|s;PH-_n3I6ivAVf8#KQ)Kf~4)aq2t= zFi0V+n3XtgWD^(+VW=X6X&vSEgop}nq{J9)YcNffP7&5<6H#G?Ez*n#z@W4$Bf7$g z8l^QVv`HkT(MBT>IF7{gJUq`M7K^@}lFb5Wqiqd{9n(-IK&3T>LV?WqL=>a2_e!hTd_r<0 zm*Lh0K5Lp?lAgph2FDeVL=cgeP|EBXW1?ytY)G}d;E^X0h9OEtAvf1`a2&__{H82! zmDc5UB?1s4N-k-w%O7Q>PJ~EI>FIWEQ)^{uc1j6OGQ_puNJf3_r z!=>?Yu4_?rwZ&1PEESHOkzJC|?uS@|1|lNJW4!rQyM7w25q)lCf&6)N!Q1b6vt9MB3}h zL=+&Zta~8@uB2S}W`Iy>(6FI3&5y>Cj0P#>9Fsx6pdhR$7q~jW_lxu%&vM{smIX1* zm1`E^IRfAJ0YNMlBN2}y;QW>U351BGhp3l~jb)ga$XX;wt1=9} z9}p^qloHQ%%htW9ayu$&gGe#E440T#tD_lmp+HzVlkswu9hi7*I2SI=z1YjVRh~pNEj89CUlp+?3St$^r zm9e_r1t}&Gfv9p(C&wxxB8rMaOF=Ac^><`LXsxSxy8NyH>2wOM6rlai4lg&(mpRv6qs=8Q15&R5G_xVfQ#E)QZb1u9ny(7#uy5P0)ElQb)0BH z1ZCZfF?fzcEEdCc-N-mpA;uW;g#tksmN{JrK|CI(P$*C+7Kq1U#5@nYh_gyoJdUtN zsDqSM6~8u3T}tIv>4hl&Zv+Ny4MAPe@#W5y{R2ivgjwEp^>yS)DzZ30P?knxXC?#mL ziaZ3ZT(s8Y3k8(YNGUKTGS*zz!}B7M??|T-Yk)H6Yh{f!5%Hu`O43_S*Cz5Hj5K9V z)fVAeM+hq+uk4Uic^TzGQUiXmNGuj3l}@3QA`HW_S0adXz7_`16#@k6V-9VyNS(-^ zc4yEpJ^>gAkqnXGp+T1|?LLi3uN)5mkd7oKgtfp0j&y9`NZ>dQuIEl#AfHi4v+LTm) zDBA~t?;{;2QZ=Tc9g4*wp;BduU>t!80+b~-L!=%`|AbvLp#-6D-uX5_D|K}7000k6 zNklT|YCyGfpvOKr4YC}p%7zB|mB1-WF%EqCP z0#`~LYnYZKf;N^w1Z59LDTU*C1fi7%CQ@@^ikD!FW^+2=$%&v`jP{PU0Sp2khd$ZB z4+rurt}oIY7m=|gq!qimY#@~kKgVVXwN_;}t+Y&zlqeAedP>-c5E*fMQAm^u?BYkR zn3NJ}ciu+C5k%=}B3nb+?O+unFCu_K(I*T7q$6>iXpU-QvaD)^u=#&omK-G`5~Ctw z3?r4}R~fOc8;vW4#0ZExl6i4Md^~tJxPg`kge2!AcqD9Rbl(^k#d8qkX;9;=PHEP6 zHsU!FjKOgtn;_>5CDJ@4kJ#ACrpB8^8tylKx1K$B}4suFgw6?%Ukdq zNj&C7h|mfXL%U7YZmpEC-h?)q&~l|9QaO(6;<*lvbdZh%0({>` zgdwq*M-T@1MIRw$r28?rt`|l5O5$A$q!b7vtT#}JFO-moDQS3V_mNsJgUGE!3QSBRRl~G`9 zs#=zfRUx#sQ|yT)$agCNRCx$Ol9O>xh922M4!>9^TkVcD%v{GU6M)v5V$nxyjpw~hc{YUD=rhIHHAR@$4c2ks%*vQ5R&_+=7!zk!gike!(*i@;xcP#;6UBrx&;=5yQ zOay~$i1{SLY_#~Uhn6vvbnskF%8Vmyc-RF5%tx~^-Zc#+{3S_x9}N=h#1WQVPkl9px8FC)#^uyOf(xJrqS)`n08grPy1 zC=q0I#Qm*%nsHEKP@x@71H zfeQlLl) z7vE`M#7VQ;C~GhZjF1SSi0d53O+AgdVRA_t z_8QtCjpI5|4x%b-Ih=KPCA*N$zGHZvYmpuiQ*(2s?$wvCN5t) z&XUw9yZT-v;b?@>B;9CLl@Y`oTXta#uIrL7_~5$H(Je#;nuS;zBf$-G#0YS7K;F3I zmt4v%YZtKRvDay9@^BJqit&1~MMYCEOlBlYm`Fy6CMi%xlav~x9gO3k$1~(&Nm8C5 zpVwURiObpkwG4-bjv<7DmT_|N29mmnC{9rHePSjoZw4_4ls4qtdivYezY7S!m~&PV z047xAjbw*t=YtEA9Ncr9?VTSa?n&IRNOL$rBSnUX#%YW>c-c{|-m;u_$K@-3`556H zx8gc3bt;Fe1$|CEL8=~4hqMI!#Bu>cCQf5UvwPR;99wcb?P{En=0)VCM-V7J%3D0T z=S_yLy%}lGEsTpI9h_u*B#A#h!s`4WJKcKHOV{88S#m$$%j*H`TJkZHg$ewLfbrIL z;sTuEejNV<@%l#m#%7G;q7abHk|)MQOXs^m0-T@otCG11iU$0~M!xjYaVESDiVaH; zCd%%$=^Tx_U*>r0I)aW4lA{B(pBUoBeJ6Omy%W>83TX_>y#jGxaVVqFUJI@X2waEe zrUXZy+QHuDM$G0rS)#`1%QbLFH9{doB@%q?Xg|3vw?KLU!UlC5LL6amIvQyjI7Z7w z>&QR#0!P>1K%6YE=bPZ#jR@(mtUiSz#|cx1NYv3>93t5_O7Y4r`eXuKmjKsf<1>APo~Ms1jEgX_}h^r zPsWp^FTR!#hrkHJyiaDrCv;u3iMFXZp@Z^6E;c?QGlm=Z%xg{IB$AjQpvi<7WtdoT z3A&{dZ3L=NKm{SbaR_vb+~6R0yniig*S63&o<%8*R|t_vl$$_kO*$P%YZ3J~g4M-Q zmd0YlJxQnxAr6iTX>Uo9bR;GW-vtD49wpa9pm)9_5CM z=aa5aGUCNB@gx{Sp%^eYFwS_f82PI2c^klh7&s37Z$Z};s|n$HJ2Pcz?;Q@2`ZZ{jFjZ->`6tpau zan6>L$M}^ym-FNMMnQ$s+U!`g2^Oq}SaKTCCicBS(TibP=L1mm5*$_zCK0QQ)7Zh0 z8$NC+)(Y&2am4q(i|PLCWFc-0Pt-4>@Bci^LC1sE$}=0sMb*_$W1jb{3RpNcz=!Wx zODbQSy%z}-St5>`8LnIx z0^?c<;#WOaDFhYOj5?K8KHCZ8FEQ z_Bbcwc47ODKp{J8pN4#vjstt9UEjTU6@T>g$9Q?=dPtjh6X0z&x}HDg7UGrr&@#ewgk|e9LVtPZ$5@z zdlAwv(DBG~Fwi$^pDXkbi(=C`N;P$`fAh_#MQh8~8pj5?=EPf()R<|!+&CY(D8Z5z zmwXsZIv^TjSh9Z)3x^bBGt-`Xb8Le5-@J_Z^V2XkJc*+_-gp%;bo|WL;Jxk!_O`F& zf#sJ#QwJM|-r}}fH{uMOn6YiT{|KLa|8iov45;9=W8c+Q;Lh7t!Ngd^ZNvSO+xU6< zj*ar@Kki^+K_{Tg_Zmk}a?7d)HeInC`j1ZTrxh*r(f-F*Upfp;jg@=3aqikQkJP}x z8Lq*5-3|2O(AbVFOz^&o>bc?CMX3J48QUf@!@T+WVfII-QB1zC)U@`;2FCf>(|e$? z75qHnXFr(SuD*pYovfp$VHsxrvPpe6nGtTgZ7oX{H%^xz2zKl~!59DRA9#rzELvT@ z7tdz6=Bg#=Y;H~xz9$ z{QPOS`2&;sJX*g3x>kS?#0HMC(04ePABVb%yQW(^E0U}E4H(_kaUo;`isv;P>+4IG6nS51FU zDIwi5eGQWJ{ABTE;3ej?Ht%@}fOOcs{#saXbN**IZgXas+qW!aZBT2Ylrwq#_nHkLdtZxOr5W^L z`R~y|7&>vvaW=~gx81aY*5+j7woPrLH8&Ldxc=f!bl`&t&+Hl97_=>hSmL~dyWhoe zfU`h;vB3M20{5*Wr*1cN46*CwQ@WAKm?k$~WarBVpt)sw8$VB@BYCSY%fZ+8Vd`5h zEJDwc0Nzt7V1t_Amw)AAp8p?@FuH2?{tHLP@Q)ABuS2FC#ckomzAQ(-@O|D~*9qzA z#u{#n2M5M^=}-QO!}AtH+oJQQw$3BGr;Nafz~_}0-Xzl$o3+2pW$0>e;>1uR{eEF) z;|@5vXe}oftcreMV3x5)*I&)(`m4&xp7Uk6)(GG%CBVT{D_w=E6HhF?g- ntp+uyK@Dn9gBsM}T;cx*vFn6!pMaxo00000NkvXXu0mjfa7~Jd literal 0 HcmV?d00001 diff --git a/scripts/local_imgs/Meteo-France wind archive.png b/scripts/local_imgs/Meteo-France wind archive.png new file mode 100644 index 0000000000000000000000000000000000000000..20069d8e362b18cf63a9c8df7ed1a539e86e6629 GIT binary patch literal 33697 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

Wm29u9>KM;efK>5#`?tbj_E*@ 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 SEIeduA3#Pz@q3M!LEwMin0v1P literal 0 HcmV?d00001 diff --git a/scripts/local_imgs/Mundi Sentienl 3 OLCI Images.png b/scripts/local_imgs/Mundi Sentienl 3 OLCI Images.png new file mode 100644 index 0000000000000000000000000000000000000000..dd2977cad8e0887060219682a70efafd56cf5326 GIT binary patch literal 20266 zcmV)%K#jkNP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+O3;uc3roUrT=3TH3aks3_uS*)2lJml{rw+&e*eEeKc3?E zKRxcgpNKq^_?|w$w(<4)!Qh$iq@};dW#)?T!t4;mOQ<;RlXYk z3O~2^)%h|%{37JrFF*NOp@+zdU&jb`7~zKVzOS$tVvZ*^zQ?!-lpeFxVvj2+PFY{! z#+G{8$%x;I#}T@m;`dm>yKjH@Tfw;V4!kr5E*5yp-+sCO^yI(!a`!GRQSkP?R*Wku zuNj6Sr~i2s0r?7pYApQp^%Z{qQ~r>uM6kSNHYPay{9Iy|@JDW?7w5$53g15^6kOY1 z7hs9FcVRFg5dmLADxn77Vr(I>jz)k6BabP^Nd!VEaWlwBWz^_ew5WUYo)+F~V~I8D zVX%onlvGnAHa!i3m2)9~S~hZMlvHvlrIuFAm0m_oHP=#WZB-yQT57qKR$FVZ?e-t-Y>>tG~{!fu}3f=!kyCEQF^L<^|2oIhkud?WWPz_9GKoJGty-tm!(D1&+fROH}LL=d`fn$`@F#sfSh> z6V%rSvCVr>2W^kB?X0|a9Ct6hW7T%^;0lgt&2V~&qn{m9^P08N&PvEd%>K{5yMOvZ zz86N)?#Z#iHwq#3lR>RK`VB4*O(tgBQPAC?aoqWWW`!NXWh`( zNqrS^q&5~ZWp~!nT!!DeOC6K-syp%jzKu*`A=VvINip1%=AcGa3AYo~5xFRf6j&s8 z&g4Eet3Ri-6qKDL?>(iDLyGVwP_m^w8golq!Lcz@n>F8B;@LIL4ncDN)4TpAt~?Mm zZRAcV&GA`x3;$@@-9L}Z_xDi88n>J^N?Zd94tFj$cS>99a8lVmmtlLX8$CD2JFmPPfE+eU5C4MaOw03?rmEF2U4K{ z{u!|#!4Wi>vY{Sy%c)(FYDB7|M{4Yu(k+A7hf4DBhF0q8q2C4GLVsH9Ajpipl2JC# z4oIII2w$pHtJwZD%73&Lo1F`ya=SvdOc0v6&9Y>D-d^)s z{iVRjXMH7u|C{g5{|_(t%Xh$Jr+Dt!Opd%fRD3(vU9d%g85%JQ9!O2!bMAp*f?luq zi1E55^$0M<5H}b>g!){FITo_sI^4+X__1?XW%js@yvnQltd!td!=Ir`DWyD|6u`a$ zN^1^K6mJD@H=)@~#kfaZn+E@f$aFW^a8u*rX~94(76(i<7SNwf z{*sU_kC3L73?rtQz5tVHfu1aepoTSIv>Ffq1~D_RVXlsI#=|05zfa_Nx_5XRtbU(~ zp{9CB@2{Zg>$jBT{xyg70q`mK2yZY>I@QX~N*{+7p2FdQKwT#$STaLhJPz|z@VF&3h+HrYrtZV4;*UI_=Ua1h*F7lnWv z0|oMqtP)6hkq4FaqTt&lg2X1nW@`jPhQ#cTM;Bc<682;0jv-LVaRV+Zq12LYK`yl! z68S?y0}_qGESI2wht{wjtw9I~n!#OaFKcgidt;pQRw<#%g#1ZX(cH@bx5A=AN*l4l zD$jYD*aA~CnM8sy@!5=9x8*XIoaLlS>cL=KwJkUsV#dx+Q??YAtz8Czy4XD)IDKu& z9frTEG%6JCfI4uqQa&vIWHk|wUSnmVASFoq0&G+=xGtF|uB75RH~hl5eL{B&*#a|x zhnH$))yJz=u&kA23m32eu8Lfk$UNW~9dsbqg&2iBQEiMouP3yyZ?N`yTvKF5|6jhl zKR-~2MPbA+5x^u!o;gu}pnpHJAEhHo7ph$hZiSHxEV9TacYC}L{&quOwkTF0HOjMG-jN5r3;eSGafMlYE`l3o(M`I|-FM{E|@g`LKW3w4h z|D+RSM5+1yje_oH7|bjx62{=GILI7|02LsnUJcQ(LRpY!tNjW}h)}A?x2r5TuA%e6 zwJD8#sueg#*YVVXl%Ea2RB+>4)BL}3DdmoGJvspJ5Ex|*0RS_3B~enU=hD~96pt_#ItFvHz~>`Ijc zsH6nY5q&Z$=x{N0he>9KB;oM|M+{){lbk}VP>hHXO%DOB8qh?#BGC$lTf^)CS`FR5 zl)SeC{vFjX&Bdy(OQ6wXcl5dr>0%A`Lo%V55FL0UXc=t?O;ekjNiOo_3O}5o!0wyz zLQXt+B>pM8H!wKEndB>O3bAzCNwby>A8H8wsJentK z7%yI#H~eE9Ql|KTy@PAKODZ&!bNx7uh;fnQXmvlw(dx@%WL%_*5z4%IBPHOor6jf+ z8C@F3WedQm0pHkzA!Uq0xXe!wta2#GzktS7kIbZdgbAxW-fAI{2*hF_*pa$OIRFrV z7ZQ=Mi~t%&BCDLpNc0btc~oD6<|Uc0DsAWJ0N4d zso|gn9)M#+lN=V4n{KTv16LCJKx#XzSpbxikr$CgN*I4aXcg;ERdGQij^Lqd**rX^ z)g#nQUGvKtFwbKlG>|V}(9`%s|6mJ$=L)NQjpV#omYj_rgW&F$L%=yN0;qQj{VOgb zH`RRe&_rSI(&dgRuFtGQ(2Fkz-C3>OSylnAj2cP6m{p^92tnoV5HBU#7xcIuC`PpPE*9>4~Ncz3QATMTUDL2)@f&Nh50fC9cs{Zp% z=~QxR(!139M$)Gl0%aEBKfoYATQb@+!3+=W-bnMURWQheiOP;Qs*vp3sZ6)3jmNn zqPw6G!XrzB6Xk`XUX$X0E*9_;Fpd#}0rsl)3lmtOo#?d-k=?sCar-Qv$^;WeI1#ZJ zkO)Q|R*jiWd>0^fhbx-$#NI*T8v7HR|L&o~hvWiv5!Y)^6s z%`>1Oz&mccm| zT7KKDfb0gni{X%QeA4(baM+3s*2@0OuHJ}^&+4I$3GV)N_1yRJ@nqax3rv7{b%i)k zV$6rC8&atWC5=u2PJNiJvvoXeQl!H(cIXYB zCO!7RhZx)}8=nv#tAD3n_$BjpMgiio;~hX`I^jV*e?UuQF@X?r;}3Jg_Idr_DNc&w z1kvZNX90%bAeSr)VC&L)30KSD_IkdgULsDKf+#3-0PdlBeSNnZg73Ozj0yYxWNgRN zA{lC0uN8;X3g{$6*V)>7fbsBL8Ni|v0;Lrow)aL;g92y+fWw#~r|ozHldZUG4BWw< zUrf~5>pY*+#cj~`1&OJuhAzUGWSoCTcsBLGE_xYlfUMcfFv|+hjAn&gQJXy(npLJ5 z$qlEj8A?-A5y1lEs+D<(rg0nj2*&HEs3ufJtur0ikz%ZMaharCH5eB822tV%ri9hG zuyWk$N+>EAdJLTfM*)(o`4X6-^5Z^$1mYG_Li!F|18b-kGfGx9U)(xu9q|t2Cu+X| zW@L6?Ei)^4Ut}Ue1`QVvk^|>6SzW9{1nJgOBV8)d$X%v*q=X{_4%AgcL{Oh`3d`~w z+PUDh5M~5*v8SHAN1uX4i{X{p8wo2jG4vMOHAA>mt9O_SOs~4_CR;Il7lku<^}tIfzv zPoDV;OUU}KwoBhGBAqMAcmN0%1u$Z${NaB15rin==@%|2T&R%mjaoino4lDtss}JT zKDZrH3n+<5z8$ZA&xWSdP<(Lu-R$g7Qnv=QHq`tb2e>{ia&> z7_i`D8VZsOF?sND@C~RKs;pNe&#aPu+=@Kj)GNvpsO5L7YHF2gQGgHLv!JtON(La< zTww1-tkBC1K~j>8mIF93dw>o9pryo=upt-Eg$kKa*aD!?>iHtSmxtEI$3393Y{i2# zzbliF*c2dNdVr{c!npu^aDFoM6{dfnhL5aHh-&*017&^FILT1F?pY529hq_|@Fofk z@-kljVT8EV8s=q4I}n7sDW9Wyj!QjA68=yduiM$x(m>Z^=iYK=5Sxcg1w-Mq-INw;`KG1~OOlOs0ss!y<5Y8MDd2=g!9&!D&H^S9 zSPgFP-D{LLp~`Ua<$zg;t$aR{WWx9aC^j{>tS}XjK7gr0%Fu2)sKit zLUs6Edv(`BgJJ5C^Fc|*{vnG?dQo8Lya}mY(=P*ZQO}IZcChnVlyhSU3u$?@JB4`| z`bU0e({AzTk=m{VOeH^We7!au4}ywVs5>0qC!(RG`jCDC9u;U??Uh-Wo;0mp+k(`& ze5yKlZ&_beT~OixhtZ{thAi@iYK6id0m$Kke8aFdiFgo6HIgvCxU3E8rn%7Yu?3s0`=UEK4PhM#uaNHQkkOA4s_pAvl{%N zu<(KlEIww5@8_PhVxtm>TS>`RvdgwLIzXfZBIC_+BO5AMdVN`*gCOx zg*y)`PLej!tB*PoQ*|UGnoW`yu%j>w*wt{frZylHRGUh;3v$Q}JT_s0Py%96U9Sar zxxCaYyimy0-t4LpTi|#Ci=GYW0^q=Od5!|crR=z{-#He^M-l=F+^*Xm;9`ZtSO>IH z!_6n6u;Bq0^d$V1z+Z3}vK<_tfSCPt zntqF0W%lkeJQfT!laAOA*%v<jEtSgIa)(%qr1Jt!WeIfw5sXH=bP&+N@{+-qWbUuurnc(U z&T~}zc|BQ(psZOfsteBq40gJH@{IX@lk2;2c4UVsw&Sabfr9EK!;iY#t z_p6F9H8q0c2&AZ!lk?->wB_B^MW`R|4gPNJ+hV>0A{dd5cZN+gHq@35G6k7bbuob% zconC<@wL5m&N=2Pxs5FL!vOZorPe2ITg_lu5;5Yy~1rXTB&7zV;pqMXI|HM?yrj&&f zPEpRPAy|d)JIzg96}+dB=?x1|hh$OLh6`%_G-)@Kr2|C9kp4t3aJkib)>hzMBOy+- z3q|57nJ^Ql~Tbh`)*X0K0nFX6F|19Sr z#EnP?=myu1ZN*aba;h1poyB*oPt#e7a|^9^p}*EVs!8NUJ6P5g;ll)KV6ZWM1e4ptN=s^79g0zO z_^*R1T$LN5weW{)%Nb&~y1ImFE-T?M5o*C9MwKQI3OS%1+Cz#($`Wu};LTvtyp{^Y z?(Q5ZcWR3Shv28wVO~^x9t|}@!kG?y09wc$K@s*sx=JK~q7Yo6DG9({`#Yux54Cde z!C1T#YH{!QB1$EyQwx^susH>hC(t75V3B*#Q9PC%HLES41;2a12C0jce1<(8r(-^h z)A}&=!|I(ny@!ee2+^dBsbkn-1b{%ZK_TkDGjOq z-hx{FYL?yx?xYkg!#tMIT?y z)l^6;j4Bw9KOzm@)+R9k#AovEz?9VNc!53YX8^ECyQ5^(!u>(jSPvLL8@uww{R6#N zvwM`FIw*yPv{OvYbDer37*-_9)U!u>Mo4p$SzBb^blyY9kY|>`4@lk~H@WFj zI1wjC;t(5cVQA}@HAqh!q6A?^28?k)XY1$<*j%_5h)ifi5XoRa9j*ul4@9*;7t8`a zrl6DF+8IOJVFl5{l{^{N zx8te9cNDCHv<%t-cCstUcTxi3spf?&Cw4^#2w~NMWD-Hg_1T*GG*%WGfcR|v(O#=l zgPzLZq1bR2cncLLgxSsD(o01S%Ielo#0*qP3&WaDKBxzw-OJdTV5Gx?P;thjHtpw{ zPA&G{P^W6Y;%U1!ew$7mlVI!l1cptiWpue=JAjPhw9URIl7P|x;nvLU0PJ2Gj4*E)F6p5BIRBZa34p~;!fRdHAN*a=;z%@}m z30~8QN>rCV5C^*R3j*Kpc{s{h0T*ovQXc5p^O$KP!peQ#l<^^?!yIM1t3!bZ8z7VX zMqw(c6CyojD7>(gSG!pFk3o$V{7HE`8%cUyhoVrR`oxh-YUqV$TXFZbO|tBQS0v_8 zE+e9@&Tg8}$a71Cb5e|XEq1s4dWKg&WCS1of>}R{)-83^yY^@N^eG26+HT?(x~H044{z=}M_PQ;S;= zfR=ejlE78dz}(0WQk4y;(Rdll&t z6x_>)s39lejh58;8GXJp}-5QjU}cSw7?gz!YXPA_K)+XjdPfK!HNR5ewc8(y30LkCFymI&RgHI=Tob zx$N1y&W)8QgF=Y$ri%sF*#licTUkUUz+G?e2nw%7{eZ6iD>_Y|3bA*3bLmq!j1QE$ zGJU)O#CV@V&?k-1rA1qO7*U(mp$`y7@?dI6D1yjnFRdMqI-G$lR%7nGa{xyl8iLz( z+)tl88E-!=>-^Db)NxGjTw62*bn^Bz1&BP6u=-#HIDm}*>^y|m^=ybcMzx3FjC||K zVJVhQAzRi-0v*L*J~G_~O~5P+6P5?&H9zTovQW+A=Wa=3?GP~-D-p| z)Y%_4;^lxeFqOiE2Oxb2PX_O`5BWhR?*t?9)D2lWe1l7*zTY zN9`w~2w>MI#t@4eeH4s>+m9m)W*;vX^;t%6eX9b9j?OGJSGASE>Vp+qdJ3$V`fMcY z08o(o18Pc`!8-JjY-uie;|ZCE^O|*C3wPYhu!%VTDZ`k#K7*3cibQ!Xd=pyvXwyKa zFGdZ}8C90vftZHDZO!=?KYzZ*5%t+7f3;f_q4{2r*^CzPrD3stLEX>4Tx0C=2zkv&MmKpe$iQ>7{u2Ro=Z zWT;LSL`8JdDionYs1;guFuC+YXws0RxHt-~1qVMCs}3&Cx;nTDg5U>;o12rOi%KlkV8R&y2u0wVDYGfbO!op^H7HaPDSM_5r-iO-2gOu8WPBi9v=-#8as7IOjc8!=jSQY@rsKknlnaQzaw6mo5Vkz*cZXpmh$_#gc4*2<5Mc}d|o(Ej2$A45QB z7pPYq=lj@k>L)<(8MxA${&EeN`Xs&D)S^c~&o*#z-PDvl;Bp7(dopBGcBLRqA)g1{ z&*+=7K<_Qkwd(cO*vIJukfyGZH^9LmFkGPQHJ^8PwD$JznMQvp8IAdlQIb;)Px9zU9mS%FhMLNSk}JlswfJgu5H(~ z7c3|xU|n@BE9gomSXdBINx;Sg6cIFt3KE1ANJ837CR5)%zdzJ*3rUh?s*e|o&#VF zJ!j_cb>LK2zP;gK#$QB9c;Jwboz=@~>N$@@yy+K6hb*3IO=S$spe#4p#fv5@it*ZQ-tn-8r?-S95D7a$veCON+k?DzR{1ZbcpXgv`g z0&syKdY(wEvW?D4h%&%I3=OLb_eka&0CZMTcQNyMj(q#$CbRiLA?RZOL&B{KWs$(N zs0ld8!B*D#T{|uRee6r5!1nkbT+!L1G9%W+vX`TxG9_GQh6{ww$cX1<_)o z=49r%0B+Y=rkRA62jKVvayXf9apv1!ZRxl8?5sGAa6J*-0pKuYx(R@vneEKD&skvK z7-fK8hY3y-ME5Y5Sz?~Ez`iUoC-WvE@hpJ&E;L;xC7<9duvbTDp5p;pLPSH_sOy%@ zmpJq7tD+3>Ycj*=)668qt3-5tceKaMCpq%%n*ks`XQU9!6NI3b0a!Xvy+-PCgmZP_ z_NWv1YdKItjO{Ms1K7jhvLIuPOxC~)g`n9TBt8Hnlb|@&y;P3<1N<{bB za@M;WYrK0Vo5o89*|CTh!l2ck@`9 z0L%vP5rDq}h*R&`3*e6ct_JXG1ipI!Yyj{x0GDxaRlOnrn|^5};K;YX!^|&tqRLOq ze7d8t?%xQQpCqE?0MfeMK_&Vtkv9H7!obG?JgNrvGQ%Lo0+L@ki2wfz4F&6*Qp&LrnkXz$!^{&M`Sx)D{-kN*>-LB*nNjcaqTc823Ti#H zphoSm3Ahx14Zt@_nwz^lLDLYj!QBD(sqYs7Z27JFgU*7&EzEcTfQIZ5I&g%>Vgr+>#v{C0%^?kB33tfRE z0H+(wz;7BH`D_2p%-1P9U&72!G4n7mSkuIMH8tF3_O(&hG5_!PV(4+l-?H>aIEC|7)B+4YkrET* zuNQ(|P>yZ;;eopW#~pbz3O9U-?@D)L_4-1L9Cj*3{qcBgtFD4O(7ClK#Msjr4UnR~ ztJ})pw~fFNX}+az>;iCML`$qSgg$nF~=Y!CI<7~c??Co zcoq{!jKb`-YdVTbJ(FD283u4Fscdi4V~nAEmo%m!#5_bKKXVKbz19EB-&+>N4bKjO&42cvM~2E4ZZ^YDWRS||QwcR*jzkqlf|$O7P0M66(qfWgXl z@B!G~#WCBY!lp!(@!S4eW&pDmGXd1`iJaZ827L3~7EHY2DqMZhrSMCM%9?8QO-aF7 zBS&H6&Et`rn1mCLJQ}&HKERuw7IcL8mCQWBk#Aqw5gZ~Jzz$%%%BHmjXR7b7BQk*p z0<|i!bSi=gi)p`Q1|Y&;(1}GhCIGiQGaVP%KZ7+U2AjSs!Vgsy$U5p+ES&Kyjy-G; zo_X&bxR<|$I1&&v3-Oz}TAhh;~A7JK5y3o&aAVbyJ_4O1Gq_Pmeq>Ej9Exh`2yX5%fSi< zoVWe+iNo;vlo<%3Lt&&O` z0sN$L8JhsS@#}?uBNq|j$^)tEkvb0V)dz!m_r-@>zeS>EYR{H1fTaxmddI4D^#^RW zT6wN1sT{AegBAcK06qXP9YA>tbw;A)`GZIp*OV!1{?4lbj8!Hm8Nmst3IJa!Q?bEN z{+bS$A-lrs*NXoyCZeVI)iSU(UmEGSR3=o9_fuT8+ zl+tJi=(JzZ(by;%6C`{;X=Is3M0XmTz$FOaI>*QM4m;=3C1KqO8}nI8c}+JY$XWu) z%rE!?$OOO|S=JhfAB;liuP`CHDD?jeY<}&VqHt%zS${H(kjr|KiBE-(fakyb!du zwZp22=w4e+<{42F@GC-mPG+J;;za^3Wac*<`SyDfbF$_V(YUT_d|u7K^&oZ-#H$2! zWV!eb$#S?ezpy&W0Kd4@XP!bN=F$G_DKqnxI?0bT);1D#J~v=c`Hu#qoM{%?2oUC? z6U)V!c>&A7wa(RF$fybUC8YkWkz~usx?7O=l)_WzL`1KUPCWn~*G@>>8k9)pQ4YtR zT$2zFg3#%`0z`B*gw}3I8fAb3A^zyBUYd#D6GTs=<(bT6q0t+TeET|Pex#Es|7~3l zoB;5y#Kiue644Vlz`X*3#4EP!kv-Zf6Fnxb9f%t}qPIzC!vPuqa0wFv0`w+;er?-U zIt&1G9Z#7|d^|wyNux_L-|5V^&$4A_UL;7&!Twp-15Qm4@lXUatWgH&3?yV{Sq0%9 zBASGzqu{BdPSe*?%D)1DYmGe+pPhM~MyMvD8_|5Tr_Ri4nQ)sk-~L@Esg0{7+|oFh!b}lO@2M${?K~ zJq-oxnk{NEL%ZSth+igI-r~$Je6O_vj)KB(5L|NKmhtd(5AAstOTL2%y%KY>P5@BL z%$uDBg@Hrf36v5Vf@>Ns1TcmSjLtv;h`thnwg{oUV?8S~OeH7eoTd@zhR&&3{*YOY zXp8tAZcEB{(X|byHOzdCBj5fvBDz!v@huUpBBBjKh*^oFGtIw)C_oQj|Ji=A3~n;r z#9#uM0VESJ(4>VgvJns(f-|2T0Y3u|yM68p9QlQ%-GRAtbz!k&zViUZ-pzC@}5^5noFex!7>-0mK0sJ3t^fPmX!Oux) zqCUZg{l~xd0f%p7l|X=jA3HYa?b-7#^g-ua5nR{M9#nyGz9ZlMkj2b*2tl6_(J^h5 zw-WG1!syJNhjt=}!WNjG82Y0z->x$^dBy~ybBxs*^6fg9XLZEY0P&n(3VrvQ!Uiej z6$oy`X-(=^Gx!81{xUHq>wQ7=6gsj4iRcfSM$gA(4;P1WCh!YGhtG)c+Kk7kU0@z{ zT*NrY1pqH^N98VdtgZ(m26!R&S4s01y3V6qYYKhMathg*Lxd2c8R$#Ewt&=Ey9(Cr{k8j(m@~3B zv+U_CDBNbt$;u#LArTGlx}`jXA#u#e#|W6Li2RZx-`=!PBQ86$hgpcP5ZvejW`5Y? zrs=-5YY*y8s)^ZIDI{nef*WbN42Se=AWL2x3I~bk23vMk)4qeog2FnM{CBA7S|XZi zjpZr70q6Fd?jk;bt%o!NI1B7QGIJ<97ehod!)$$l{R3v66c!_o$$BecbmoAAxLYyZ zA{>nMEcwDi{=x{$;PfqO7S2S3V`DPXH+hRowuOH87MIv#GSaos9l!)SS_rx(CL_JU zG&pUSzqs_E+PZGZIIM<Yx1JwbF@P{`>lOU8hA ze8nYo1}6A8=J51AM0BPa*mxqkK#-Vd$w*K16_>0zi273>lab!g+3RZ+>|x25I|}Tt z`HD;YhrAEar0(4O0QPSy>nBLOpO7=6XM{O;O@fbO;C;j6Wa`3puU`rr`Sur?d1>cV zKEE%eJlRnYex&^Gf&m1+k3itl5!ocPsROT=e;xmNS@iBHv=07_N>@A)s8k;>L z!33c@3kux_X@AZjA=b1Z$lIlqmpg;&5L$%a)vaOP;*xSpMtVLG{Rw~#gq}n+Atoc; zW64PW+E-i>mUj;nm--@~9c2C46_b%}CZbWzoqW7W5*IrP>?Ob3L{O`elao0B5KD+~ znqkO_Byc+LMm%NVS2P-vZ+UlSzD6^Wo>eiR0@Mv$IYGp{LY&ZNr*ESI6^8zjH{;$vg!lo+dQA zD7vJ6)!M@j_5T0vH-8xK~|9j|vbZ?*m;!YY#rW76mfy=@8 z!I5u&D>^Dsh(Z*i5QQj2Aqr85LKLD9g@Ze+7wWS=#E-KWpNnW7;1Gj;uRkI*Tl^dV z*MK?Sx$fKXj(;uM2f&|}mwPsdUI);N1NuwlV<$~RaGhiTh-Ly03D6Sa&P3=IH=-PC z#(=*8=$(>#yA4F|0vHG&o=n<(y`Q^l9D-N(YrWpNsN&|_pB48v!z(Q>cNFWor+nO_ zo1jY_&98yoUv_cs9|0cZ-M#mVefPcKGW4|xQWs9`N1ApuKqEjn91IC?G3)X(Us>h5 z`|f?dTH!6*8S{apiHi`tBLQHzSl8FNx9wV0`}iyuxYcNmkmameDglhH&UibX8`^B2aUaM?vz}tiVa~`*d*qCVu zUTEF&`b9>K?rOY$@AXgQr7oI&E@|Q=w0`TE&i=rxx>bdf8|SR74M_n2q%E285`s6E zL%O|F_FVVab4FDe!6@f}XaxWPDZebcYSKkuu0!x5El!zov#<^sG#SBrkz3oq;gpS@ z$$KU|J{!!9AqEa-plN-`q`iRPeM;0q-)p7EJB@X{QcH-N8+z>7QL?D|!IxeGuqiDs z_a6uzHVH`iL)n!Nor~aYZ5{yJt`nHLXnIE4k{KVXa3ZsVv`Zng>Au(_4=?KX#-kGv zyb(m}Ox~f-zwgsTY|JtM_n@uCtAu1NA#PUc{3#1a6HCzcm3JDMO*i*ACu^x`dw##91e|VKVlgjUdF42=10n3gO=-1El5U zW|Jm1hc(hE|&JBAg*(W{TGU@d-yAPKv z<_jaANw06O^mt1lIc$+9nY7bVuD#$P#VnzLZ#wdoC0h`@z$&g~e3>HL@PP=P>ZzsW zwykdc0-Q<134lauFYUTd$da|8uB50O~*Q2^$HKkKlDVh7L(@Q#-uhY>*Hc zZTJn5$ISLs0x5v-!;>P3e-4P2p&K?MiWtjPy(V6gxc7&hxA5ND%8;sTRrSw(+y=r! z#%F=*`i<2KRxS;!{iX_lE8%aK_DMYb?lrTC}#*~zBP0Xcdyz5>1sSC|7cp%kpQ~Mq_z`C>mFq_XFY64N%4&y+bv#4hA zhj01UY^(&(5I_Ec9<#)?YRK)~;>O1ZBDN5?v)C3~N za;`Cfp*vmMDsIW0A6Wa%Rs`=2Ds1@A(q;9lzqk?1VZ>u8z14ZEW;f1x{~G|C0j%?{ z{%XzM8>jwLdVQZ6v{S5Y`0-;9JV46Sf`i^xq|>6b^}6n&Ghw&|Rl2>U756+l$G>Ld z_XysPxX$(FiWN0)}`|<u7r%Dd0zR^7zmMv4qkWOsg$KH{<@Uuw_RjVcda29cIS>~ z+wRX1yh-dk1aB8D1yJJp?~0A=54;`b<<&U8^}TN|LGV_y?*Z%vPzGR!Z1lVwhEGz$ zaR7Ru`T4!*Eb*0T3A@F^`*Yb^INW;?ym@UKf){U+y> zE7k5-Wxavwx=$i}fgVcJSh`2eQP`RO{)(D10DIM^(SWxbMB!)nY9a}M4T7?ak6mAV zw@wke7{L=nJ%)BX0BYDDIN(b>!&G12|2$*?g`XrD;Z#-l_w4(sv1t1@jq^SzQ^fE# zg-QuArlec`IwWRL>M&Ds(kRkILNie@nGWmq_|2E?yZ^=4LZL{6KR}2$EIF{VydJ@u z#=Q|SvxE?lyLkMH@Ge7H1_00Y-SyT#9A+e#)nMzpIx|YmOo3y>zA0BgP?$L}_f7c|faR)s8-EMJ!b+(07SQ0%ng9am;>ZHN z^2$%7%Ux18bMEH=wjg+k<3?p)e^%-#q~^*HJbrC!VEuRNVbTiyHT6rS$M=2MOXIA^ znMR$MZU{#@H0RNwPElCdu)(61k3|fnY3n-U&V&hw!_`())2ZYaGaI*cQT+86rnJ|0 zGHGEZTe{rNj*eG20}zHs5}&>PIxQ(-fU?0qD+jDjssE6D)zv3-NOL2AS}ietw>hQ9 zUp?D*&6h6s7smG{WS(%mvcKIafd>kV{YT3}#8~=?Bht+VLhp|V1OY@Rg^B3euU%~n zc&9R;?C~1M3GqXZ9;y7L*3saaW&10FG!P{+_DOM2tnVr1d$4 zOs2lDnl;s6pq(6l=$RB|(;yJcmh^rjS+ZMtd|R5#O>)ve05%*XGm^~wok4Y*l5e;) zOAUM&6QnMfdIAhb&q|N49CeMpAmewPkDcp^))?~==3YI<^mu5(u!wAG(*Y0Jc#>n;b9Q;rczGVdVn* zYKxS{bA#HKxc0#H#&Re7{QovEqb2@?BcAR1>Vp#uxQM>5O^Q#;%Y7I`v%{o+(+?j4 zs8vp5yQi3&3_~nby3wt>+~t=3z0PKzuj#;L>)`Y;2wom-F--2XK8LViYCjNNqHI*M z^msR!d-puoUsenuE5eaIgo!-fZ)mA(Fs<>! zZlABb@+L4hgokJ1MAEeJAj*#LnYeNDkBj{I8%kBqp|M#+B3X7=t1&m3Ro9y<>Kixz zIGzJ~QN*aGfM_^~PH7e@@2lHTdGEj80`&G|-I;8wY#9B27VdX%e zp3LU3kkC3f{n1`e-EqEBMMitngkqn6-)Bi%GUGo0!XgR|=so}tR?A?$zG1uG&{$%K zk2xaAw83a&<+hSm9U9r_`Bb$NT=Mk8&h2PnY83z)=dG03e^fjIwT@zgPor1e_1tF9 zj*=@_*LSz6GIZV5@YU8g>SoSauM#wS0r)teH(k%h{y=TR!j+}1;jK5ge=^v5_Lf%e zHsBJdn>lCuzIiLI2-G>}g1OauggBr(+}{^3uDEr^9C-XY5WMRkw9Wu5+1^yv>#tDl zH;uU#7M+b5QD#bvzXwES+2}157IRP1M7+^CGWD=sm~i2!{??Hv6gRF~U#A+Pgc~mw zz3;p3TrDAP0e}md5z6J>ttG@A9&CmJwM*B$BX?KqR_Zg1OP%km*&I9Sq&}}k!`$O}<)Oyl{l>k8`%pB0`rQ1{DFRNTzyRC@O( z6&2~ur5|sPJ^jRDz0O%#xAODlfz3aaDqI|qxVTn)?ui+8)W_`cxf{1^|HS$H;v!YH zN<~t-{!^*T_1@bz zg{69e^=nI?WT`ha*43?A_io_3;@xU28vw{K=}nzK4uyZU!Sk09rneV@ zQ_EJBPA^qBg)a&7?XCVS=CFQF05&q4Y^+N+ncKAVgMp4+j!Y&~(|o(Hta793q^~6t zpnj`WND3tp{x4%qJ_0TT$ zzf8-^?X5SsJ$olU;p#u{2`ibj!O)dPBVzB)vhCFmzv8pq`WH?db@D(C=)}zA{keQk z%_Fb+Tk}6u1d{|!oD3;Npt^ob<=>yG2)(!eoX68h6TQ`Yz`Atox%SZ#05Way^dx2s zfUXMw2-G=CDsO)>005~Ar^SMhTz=K0GD8kUrY@S6R({o_-2i0j;^_nHKPueq`tJ%3 z5wriBldZyPIhC2sfm&x#<*hSnLiPL4dAui?w8OznAOe6u`PC2qXlT1P5gpHxIk2y; zsPZ3A)kQK&WNuWtx9)fr%zMaeN|s(&`6(HkV_d;synVt#^z$cH8tL5M{VwAj?E$E5%gY^R@eE`<0F zns|>)+Iz7_3@q*c=Hv;j%|@o><(@`n(>Cbx4%X#A&3%%;?Kk_8%R@7UA~xnHXxakx zo<(F53zZ6w0nw8r=%WxG=H5Mbq%N2`1Aq-uo=8Hxr%Z$e0HrRPK7cgsE2ZMCBt%}q z@Z(2?`k0oNdxnU!7PI8Z2zKlkz5749TrW9E{kt$-#LfoKr~y_GWcmXiR) zrY)X+2?_064(PYDF7Gqfu=N1-0)BX>iD`m0`P3_ zXYX`@`LxPc-+Bl@3QLZcjqd;Loj4`WIQ4g=egYy&~tG@@pUcF>TTGm|ipge*V7uUn~GnO@eNg9`D-#1^}o4GX<*a z`U2?H=f!(HAX@4xt+>7FuIF|D5VqSc?-PIWk$+KsLphkOzKZHstL}Juy;AETlMn}@ z>jU-9mn&|Xwi*CiugH#Lug|0Ww5pI6FtuQp0u?vse$a3B!;?i^%%3=*SCL8U-KES% zUBgbTr>+0KS?|80BtNt?ncE1pmc$-8=rRXTLPVDb>YcA>@v)Zym}g2(I#2ripXy(1 zjc*F2hb52aH0iFZk07(=b$`F}8}IVZLo=dYg||g9MMX@;00N)~ zs_T*<*(SZd+2+0}R|EJu_Np;%5Z%!D&9?E%sgr8TJsv=;sb}IC_6NSIy6d@}im+AA zm)`o>8Jv)7+5s%grqranuSp9y8n(J-{jXBt;bJKZHCx;*z5jK$#kKMKcN^!eEQ#y~ zTA7KH02=q+F!d$&`B&&p*P1TcexJXprnqX>yVC=OMI{KXXxZ1A^oMFmkMCVAA@1z> z@fREh!f_S1&UhC@F}B+;A4F!;<$jz18T+#}7Sf zQrz)JJkjI)k!L6~<^o*e@|O7BR_4)j4g3TXRP#B*39r1Z14hrYx9o_MQqHe zapTTQOBy}=O7;he>YrHuI|z{^TPcNz6L& z21R@)0B`wKlU@i^*H1MiC!O8%g3~`sT{vxe!rv|pIi9jL6JTTlN5###FUO7@lhN~{ zGc!qu191k~yQ}=8+Np1>#r~yS`&zaAhI)G^JpN@`UT&N%>%{A1qvsO@bdzlKd>DV? zk(ZFB9kcJrH@{JxaRbDGdguEUf1AEkIhQ>Y!Mw`x+`Z?z$EE;q(ZIgG{xj~#>v8|J z1$8s${)EV#LkxfA9Z!D%V6)}?%)Tjq9dn;`$iNu@{;9lalwbYu9m>nEQuexO3fI54 z`sK=hJhiIF{nz%2Kl!NVYnOi#!qe$^cA+z2;j}zk)`^#h*qGtIJyox%YV}HW8#JJ- zB9kdY(ZXX0@1${Z%2!(X-+lK!|AoRW(O}YimA60nE`UPo<)c%3UN~x!ZN%}D8~(HO zQAGoZHs<0(Da3mB!c{-iE?sk8pr(E{OX=(==JG>y~J?xQq1$*E-%?8*7w2rRtsP9n}$GBddYg)8pC;eI&># zJw7&MG!0JRjESl9r;G%!$G5WZ2LQ#;r47NW*+QYLU%jKES#nB{1mLQhId=mG^e=i| zbmqBA!W#hu0=14;O+6CFf~fbtd!FB>o~xZY)WnYU1?xW~lNR6i)d$CFC;c%VfGKV9 z^z+m5az8arqLH9b=(*RJLrBY;ad-c@kDmlWx#z9bTL6@DKu=WtXqwbBwUevX0l3_G z1v}k&1=GcG8H;*eaZZk@SI<*Kob?Z6Hl>gz;=vq{K7S2+e3gM(N3qks`D^d8&-N$@ zDOJu^Z98hexfy75=ZS>48)}}N_XB{&efPh(H7zf9m-pxLLWNr}@xiDz^|0NRwq(Wx zL)vp{`M5`(}P{rf;bmI}um)_b3hJ<9NB1pOJXvj4sC%q2f6;!OyzB)3GKtl#r#a#;u-x_zjiq68)ih#<8*WaJCu=1P~LDa^=(s~ z&~4z_R7?6{T0roYAi^p>cOhqPBmUo z*~5W`q>oS;%iadUNhM4n1|F;&TCXAc32CMj)pyO%j|zpG56yPCw7=LYwD8fQ2yaz? zYX-C7RRiM<<+Qsn)f-C7#wy10s7zQwlb0WCUsKGOsOp3gDGjQOMfn9L1D=hB)G0^2 zGbR+9t3HREE01cUhrt|#vWuZbn{kHhq*Uc%j6R7Wy~~DFG8u#{BuF8KOu+tKN?wE0 zC{&*l3{&d+9pKqc6rvD?C`2I&QHVkmqHyrS{{e>LR>jZyhqV9z002ovPDHLkV1nYZ B*uww- literal 0 HcmV?d00001 diff --git a/scripts/local_imgs/Mundi Sentienl 3 SRAL Images.png b/scripts/local_imgs/Mundi Sentienl 3 SRAL Images.png new file mode 100644 index 0000000000000000000000000000000000000000..d54a3ea4b142872e7a7b6d1c98bb3a139dde0961 GIT binary patch literal 20375 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 literal 0 HcmV?d00001 diff --git a/scripts/local_imgs/SAR High points.png b/scripts/local_imgs/SAR High points.png new file mode 100644 index 0000000000000000000000000000000000000000..16019b3585407accbe7345206a7aaaded150a81b GIT binary patch literal 16300 zcmW-o2Q-`i+s08NcFabpU3*hv)tVo;8c2VpDrlKfCtc)TXG-`~2VM zoWwcKN%AD$bzh(Bx?|K-}b227{ZW3?!bskgoq3h9GM4!&zMNk*u#HXNF{9lYk_ zrf23z2y^J^NgSvMkE=)EA;PF{k+=5tzWt$n_sq%M5Ph-nlj`hL^mGkbvVM?{l@HIh z)yaCAncGy4fUX)OA&#m5x3VUyPfiUzyKR*VsrJUq+YLUQm?|S>M@N<3BD2oqzYw~( zDs^&MV@#-x{Xv*dl$hlqu_cFKrR;L$Xk$Ac&CD7ZCm4zzaC^;aJjzlkD)m&VX$sjl*m`hO0<6RDMd;+12A&#hmUd#h+f;5(ge}Bw z+C9Ix6LD?77>nIfJ)X|7D69A=Oc8^FTyql%sOzvB-XvTG9z&~$sBdwM4&J`V63GW8 zul=#TpeqO_|M~OO_CHFOmRC3*$m*EeG`^Iou^^w>Tq&cOZIJ#kzV>rC+rC2nrLLPr zzOey@uxtLyo@n!L^?$fgYU`qU7vdIO8R?1uMg`MFUf&%F36Jf659GU?Q!wyP3=aim z8H`<26c8^xQFUH85)w6%g0zH|&(dn3O)CAo=bC@DhOQ^xG_R>Etmc6jX6S95W<|tY2htHQgMf-5mT% z#|H-oE84~v9s5+xZKb6)){GqQT4MU@+cY(euTT920&d!FEXD;Tqus|LM5v+eCL9@_ z#;;{u&CEi$b=XJ=Bx8%{QxxFf5@HTIc=s8)7-W0*1@wX!Y!}~wQ zdDSO)BccjMm6V_FYD2Kb7QLLY{>b1Y)I(EbE=lDk#fpILUtV68+Ofp`USFSpu%;=( z!P?sIm9Y`oFGoFS5yIg(^a_7DL#mO=AA@V|pRv>wF=?JvuD$P))AU{Y?&BH*y!-y^ z#SEEsY&1yyt;aSfII(}3upQz9-5;~oOt(@eey0!?w`unn6)txnGGY4k;*5&q@TVGb z%&U;x+Pr6vqW9vBD&V75%QcuiR%)dmMs3MM7JqSd)v)io=>q+#Vwi5V+{6?cTNGUc z66k@1e@Q<=1jkSfW*)VsYTQ;9F+JzCQKRT_gcqdaS5VJ;V$yt;?Q3#^tMTW&gYR7W zPFxbkphAGz^D*jqp4ZW`#lWrj7==ZhVoQNhWz+-Yw(~WR5JyW*pSc;} z(M9sixDk?-zG~;8^*BXaxvHwFnu^6!wyLW5`|So*SYZWrOJb}a$07Ajq>>p|3a+LN zp3uZL63_K_3)g zE2Dt58zET~PZgy8ySbr*#|{)(>1UT(@sPO~1XiA$B_N#YVvNJNOH)~5Q7XGE#G6}G z)$UwWxUF;DC0%F<`h2+i$tfxETH8xBhFx=gMDi3%L?%Y|FYDNbEV#9`&EL|T9}9^Z z@4Ly6NP>`A2Ouf*!=9@ox&8|bJ@T`j#`@`k`@l>2l+}sdTPP&IYq5lqA@lykOxyLB z$lGs09AS`#QGc&!?XXF3&v4bLoDF)u%#R{I={O7QDz&S5NDo$bF&202%V|aAOzbn4 z3|sP$#`)&yvZN8qO!18g7!j3QZZwpNv!@nO;Agxnz2J8dT^Z^i`f-WMbM=lo)Hc?ieGpKr##i zBqG|!Wl@_+4T?iq_|9`t9VMKq_)g6$FfcIp(xUbkBu<*PR*wxriVnNj*fb^(T)b_P z1JR8O$~9Om1z(E2+#8>D)-vWugT9K2-ra7i;^BYpxZE?m_s4+1!o-)lYTkc5>cWQo zn6hm!sIujI^H~Ub$>KyL7S1xJqaH!iSzv|0?Wp>}L>}$3SyBjCH->e;I2{IBuNiwpI^x00YO zR2m+!E|(Y1pum&Dqfb9KW!KLZ{3x&z8tt+R%-Ke=v$AO5I5;@J>TvvtVMZJoJqyyb z1k-%j5n=QSkaRn1>qzc&m9n1C4*WSTsiYEZc=BMa3<)CQ73>k*SGmGIZ@QzErs?6| zrtQSPKwwk0Gh!UZh+d&F&V7olcAI?ve^>qGQ! zW?|tJhi8Mu%39-?xql0yt)^z9^%JmKp%hI{{^sU1gfYO)ECje@XZW*znCyi(U?6qI zoUboliu7@9Fv-vo47rw;mGy19h)`k-naLpMuAf=P-_az$u-5E`FJTsue364vj9c=g z{?;g`A8~_Hpx;&98h7r8bB*lVoV8!JQ9bp`K~Va^bV>wU(b&;M{XWGy{ug)K`Hwp` zP)7IZwUd1g0|X<2~v)oy3lZh%Z}@ zayrT1t|bXZNU;CBAE{F;Nj_>_bewDEnzGf>)|LQoWIfKVe+k93m>GeYQK4n$=X+By z62{OIV#+SwoVLFDxK`*;tz}6DBb>=PZ%c>q!b=%12{j`lBe{nTJcQnSb{l6OF`&dp zTxKCNEW(W!3ZH5qtnwnMfK{TbofxU?+f;F z>P;aPu-px!T3{~72t!r@?lh{efL0<T|c6TR#sZv_x=3+m)(bY^Su@LBXQ_^Sz2pql)yR#{l?6$8*;l(?+Z$ z&%4DDb3a6GC&Tv{#!bBLtavr=9+fud48?e$(C8%n;J!bu$7Y?qM_%3K zbzqetX_>%vgEo2nUv%@$e<`XWo`33&ypI=JR>+K173t(Z-IfuHPom-7v*(bCFIR6g z7*sxQVBFZ6??&bz=uq8(@+(XnQk;94Zuh1&??3??&X+xwK9!x7_mM%f&Fi!Gzrg)q zkRy&1#XmIkAGZf(c~cI1C(Xes#Fi^fgGaqt;S>qMe#iw#)0T#KDf_;uQ#J`A*Evzo z7f)xw&M)Fr`!hqIm~FzCcm}y>AT+)D`(!_lCJh?BK3Np$RAPnk@$)zN-*CVuj~#=| z%(|lt9hU=68s`OJue)})V4NL}jqUh>f4tEoOx%UTak0_UUf574caNQ)M3hxkR|6+R zi`&o;W#LmO`Kw8Mfso_{Y}wh@8aj708dM#&(v19_ zoCNPvXf$n{IL& z*>7i^*o<@cHLx0cHxOx#N``qfJbH`Ce_J)?6g+X6Kq-1({1D{wC!6)($uc=mbq`Vy z!6A}3<_uhQ)-HI6`K58_knvEEJ|zYX95m$I?sd3BzW?F!U?MUVOb~tc3B7*ti)uu> zW1(6IACek(;5y$gEkh|JO-;>419rj~@jVmmS$TRCrRCjJTrmL!&|)q>{M9^7)&C6F^5!_rl3K1pNm2A3x?xD<-=`uzWpXapY zmklp%T;4%?1*5#XKL(nnal~;l@_H!lEgI!au|-U=`5^sgpSX6Q+7_K~r@#PBgm|#T z5@JQvE&gDNm5BYQTjBmxIUy)uF!=H;7VLeni{W*)77E(3CI1z*FNQIzh|42(l3}Xc z)Xv^J95Y--8N7Mf94s-drB2NMO*h=U+%7xa$~<1PJXQ6xGCgn_TM!&O41Gpky!WMo zYK~b?bK(3_LBhkcqwek$uzbZ-f;bAg4t{JVU%zN7+zc8tN&rL24H>`PBBuW4@7Y}T z_0i?+R|TP@+p>(`l?#k#D(?rL`R7y1lQ%lC5|TegM8xBkczAiud9F5gP@A%V|Fj|; z^hvpVnE>BDDZV=X5) z6e8qJiR?1S&G1Dc8TA4oe0uk|-wBSP=kHn2<5s}Sd|RWSQLaUs)O~bLl^!DUJp5-% z45+lf;q-BdON0uBvXye8Ph39#<$U=&o&NwCpxg)1NkW%QQ1XKDT2Z-pcv|-`yQ90` zhf#y!4C5*@l~vwd58gywAC7?CBLVq!4I1;3p?iu`ABx+#Kk?ykCz>7mQ_s)oq0 zigfan$qLa2x!L>ZULSGr3gf1KTU|j13d;@Ekd3BGYz2A8zJf{`E&LV)zcwa-UbA+U zS9^iW)@WOY3Hm_64>{hC!x=Ap-)t?*t&e`QgJ_nU0_SbLywbTz6NiXm`j^cAyHo9s zCAWXtuw8DThC_-*A9AXn5?K+QO3rg5C2qx2Tn3SB3I@<)O%W8kzYQpDmKQW0edFSclz6zCMWX9Q1EZhRo4~!M%Tw%hv?!;eABOr1Js`z zSI&%1K^&^IC_S|DD2BMr=SffR0z~0)*aI5n9y>GGQ3xT&Wv|LvH;S_6=GQb@92sa5 zpr}5V3E+@!Xi&6*C7s)c9afMo{Ws*AOSSg!bP))93SZ=}Yw^Go;!F%ty8AoAIq%RI z_s`UFnZHLmxkQO!TQlxyiS<0vY5CvDG%Y!pdtDSw75Sg$jj+b)gGvnu{>823u{VxW zx_GKK>4K=b{rMlcbAoiSTHK#$yk^|k^I^f)$9)8n_>YIr2ah}KXa0;*l(LpBUYpS{ zI2Up?$bEbtyM(uF;&{DFEKLP@cfyr|>B!}+K#~$1D9*e(pFcaZu@SF7o!+!Rod|sO zxo&h0e8_>pgg-niuH%<0%4rst50`HynNM7Yp}%(BIrD>mvCH0d^W_k;S@a4H4mRar z9j;@E!mb~%D0CZPZ$>Tux%^Y8i0aqf;*wJu25coKE6dmY@Adj!_k*Afgy0t@$EElN zlbLDK)zy_HPnP>WLqgZRy|c5Ihlf@&eLj2>s!}=Yca=00i5|87uZg_#rbF2KY|Z@) zKgeTwNbI^7n={Yf-R&&YH34b^cNY=R^?FOV!%hbWAc2!1U9q4twyKH>DVF=9<5^yZ ze+YPh+XC)8s&vzz*y({|T$7DSU|}}osuq>jsA4~LFQzJbz4LyaV(nIxo5m+#f84lj zuyS^(RyU#9KvpP4p(J_O5=-*&=4JeWX%}$>=sx{)?1MCX{)*P1-*5;R+fdV9s^8-jep^Jb(4njA$7@>tPkBWwC z$_5IX__>VWbaC7t&ElOm3XXQ3tnBegLI&2&n%}{@FzU=weGXlfJduNirhVUx_Uo#; zACFgzVjcIBn`%tom=x&=hj;J)+x_p)ACTONyMDRWl;=~Lu8BIa)y?TDz&l)XEov8* zmxXzF+T8;{CX1Z?e|{F8lsWO|9C-K<{Tx9@r5@o?i$juMQn{X(8rEdw3>q28xn$08 ze<7A3gZ!D;y%+K$6#IK#oMwM5MfD7(XN15~d9=0n_v4&9?@FoV>nJpc-(|_m!PMbj zDk@+wn9CbtnXJ6LbSr&zVy>DvmDitGC*4{^`1pK~RYO$Qr9l!MEBuHLgMSwH^V;@W zrIy_^xn?M6Btcfhu6(?r?k}8YFUK}wh8$@R_xFV5GXb^G_wPM7HbRvcQdG*g8FGuK zo%06t=;1qV9R^SejZ`fb%#Xm^|M`iq%WgDypQgBb=^I~59a~pKpX?g1?rLs+{5R0} zVxFJ$nqUEHhWS`DcOq$h_<1nE_d|Lt&FJeFL+?7okyyMr%q;;z10lLPtgL3KyE<_W zJaPzrXqK(N+r$q(cL=@_v)Q-yI;IHsury2~fMZTv-_VXud=-WXlQK}yM1sLW5w?84 zQ%-15$SBDuDc+1)`}bDFLUK4q^K&xwnAnw+m616x?aZkbaf^6zJfx60BU;sw0_YW% zNXa*R*N9X?ysN3HX}K1l!Mbx^>-4gW z<1aL5e_@I(wF|p{H7TY?(Gqbo;nDH@3x*$fc=JJmddw5~LzuYUJ~y)ky!ZRyj`{g{U5*TZT9s(%bBEkqGxkP0=OEdt{^;P9T-JU>muWl&Fh)&+_~#$ID6LHmp~keaveo43zO%e1&^kk`Q;vY+R_RP$!r ze+cpyl*|#2yPe8*YD&Xot@4=qS5sAVdR+`u`pHfmc7v%GMqnPv)n}BK!~WIp>1s&q z--Q``$G_67`ROlcn$Kw&FG4ax5;@&ux{?m^(dp}=z=9~vACuwA-rw{OHcNC9M)JUvcdv@~r+1!AR! zS^j=?M|~rg$L}n$Qw*9;PC?e)brMN@vis)>)Lk<^a>0|JGggfDdS|5{r*UezIv8Wm z=PUCJ0>?^?tXF~?IQwzx9=)k4(W(8OcIQ`_s0Smb8G zTAU{=ARxfaefN9nHEY^dS`^-U&v5rJLA3NCXYqhWR0$iII+l-HGNui0B?@V*NE<%~NA#@YFxZ=DNsZ%S6y8Y61DCV~%^l zQvT;g0|V%!ZJ4yH5@05`v6Rts?|=Zt?{5=nrfY)!_T>j0#F^q% z612a+GFb?|cs|I=D$go!kz8wXeE$u-YZ+sbrh4Y|d1xiYx&8Tmm337EZK zV~=Jjw8S(sl^8J4@1vrkkV*6MvOfgwd3+Fy0{`Y4b7rV(==E?~JWew6x|5j7c;dgx78Tdf9tdWi` z_Vl|iVcQ2Gz$@&2)k5S~d>z3Vu-P5@>ce$CYdsU_Tj6}?z3$7?bbhC@x^(2C#^;!c z{D)H^!=7>N%ZR&x57kG0e5B+Q6!P@R$@J)lpT`pw)YNQ|zsg>fKF@ZS%rx%>5Z>Q<@72f7 zuiM*(5}(dnTIRv~kh~nMvUQQm%lxOqiI-!}l?kx&&Md8KbwWUAmKgUj%{k0`T3lZi6hU?Mm%ZsnJ(iSZ^F1AX>BNI@V-Mfn4aBJ z71M;z+Wmt%Z})?rO?x-bO#`p1tJ_Y6w5QQA+^4?p35(^r(}Ga9-CB5fc`J{`T%f=i zm8n=JtWr>8p-K`aKVlnrHcs_iCU&Q&7iX3rurC*#NdyO5fav07KD5GYCpuSEKRoR` ztUZ}DNWNyex<7I@>p40YYv66m;g|4H$7_GmP2jeOSFD>6`|_$H)Jh+4Y;08WL!V>I zJ%n;jrucBTn^sF3G+&L7lJlEMT3eS^ckE?Tz1)6)f7x%Tzgx^&Tnr5jb%D}?`s>(? z=Z~&sf#cJ|ICt`{wTmH(TM@KJ(fB~kZfG2tS;Rcw^|bUrb-Q~ObgP4mnR66K z=VcO}Cvl~ zh6PYE6c-mymW6WbRL@`D1$3<0V#MTrOeT(z)z(g3z`SLLd>5 zjdy&4^C>90y3?;{%9g3F-R6Hap*Ktkx*vsada;E)Vd_7&EW~EpFIYVDbfJ3MaZ&0( zudox;9ELq-*oe&^@*0467d{lkqEIxoGu!OaBR|qc`^Idi<#eaSgalgy;!hUYd#}50 z&)yD|&mKjz^yZNxPN@C>3%D_Hi^pD!%YsIGZS^y1t>N0!xNw*EQ2z7K3)=|KB6Lb` zrQW8wq5>S(%({rVRPX$_Y}$2sK0EKFCMeAud{sN-Sk?&JXF3rfqk;3Bg*E9Jjcr~y zpdi(Ujn6Wvr6qFl7~=h~tem}Hn(zEqt{Pa;P{Z}1lc;KKH?T1=zY*8VCseA~?l4W) z`1y7_u^mAE{3!&iq^}tm8Clb?zC>ZtKnUU^o*r&GF9tCWxyPX;Yoja2BSWSM3|njk zu3Upf)F}2I$U1%z^lut0-9+WUAObE1khu!#C^Ve)DH#5j4sJZ|3#EvWEe^hT9n38( z>^}7z2y#zY_#3}`*GS7I%2X*s#uX1;3U*mfDpsAyuokuFK6N@~XZ-7I7H?RkQ@_~2 zMh>ILvH;LsrpWmSM%QDBSPw)PalQV0*cH4p7u;CGMHMHPJlvlw!Y(U8xDnCgO`0>O zc8Iwp)EP%Np5(L~Thw4+O;6~=cX-0@WLfDM6UR`vZNkaMM&W-jtGfD+49-PEE+5q3 z{H>y59h%H-4VW4BcHGySLzIiPuw#L)yX$uur-{;n!*e6gkKVGq=T) zG8-wmHo?}F&enmP5s!E_S5)+3r&u-k;G^m91^@yI$+&#@-cl~G`^9H^OyJvZb$G5) zDTZq=gd>dyU*!CV74b8dn;|-kJ{Dl-vj8n2vr5&F055m)^nP6VZ>$r#9Y5vO(Pzz) z4IQ!+CchN_b>f>RE}cJ-HD-+wdH-^H_5wI$FOu{W5}-@ZTmcd88^3wP#S!f6=k~a2 z&|m(^oR5ygL)IfAZacq9GK>L}=x+ub+N~c);=6e}aEK{_Bo~G0rqnfE3 z{5d8^KkR**%U7?$1#wZ|`en6v$io*0sd!VF=Wg5e_d{|Eg%vSO0BXyn~fENOt z*_4M;%!Tc}L8;s&oAeYIa|bd50Jj0M03_zsQdSeY*nNF{4!R1T=0uNNRQq*JMO`<# zgWx_LIxjt|H$l(;#Kw2g1&1bsiyH;fYkivpi$0)va!J2^rPsPUx4x z-7RbxtgQ&)CRPPErf=V~=Ta18VCpjJj?14KnKsV1a!|uIDs*w zo2m|eB5z)Qj)FATCqPoX%I{*QU>Gx~8N{&BpA#FzV7VrzqgKSxm|q97S5cRk02h=R zgtC>(GiTpu5V`r2V~fJ}?UXGsQ5g7Uq$k4-`@CXf`jB;m46v%iCH1%1YDq47-(`3T zYKcIdqeUK$-F1yU(Ry)c5(HT?4pl!qKT-w!bFZT>Ot>5r+{#^U!j+kiV??6qXUc&J z=p(f17AYl&lZR0~VpeGdbGQd>4HJnZNl6tM*L~=6#!i%E$8vr7Kg5X_=fgY=T4$o-ah*Z8DTJ78>BAdsci1EOkmx3=H-p#31aAumPe z2<*G9*W46AsdT-SN`E(e)TI|{F>CK1BnDwJGs&2i4RKbMLYUCpt z+Kuc;pkkw;w*Awy;KS+s<4Iy4-TyH8?P~SlLF_%_Bo5t%Up*XLbYiXaC#ZZkmyQq@ zl{6VH-*gO=N9wLh*X*+MQ;7{qgxW@#EAq`I-Og5^kbpg49`~=y~6l6kyCjNTi68} z*QlJ;TWD3*8%f5|?rE&p@eCrE`eb4_!`C8mto)jj=>g3+ZN|`HzVPVdF~CkOB$kT-Ml9!k+CB~kJkVLi4(vB0+5epwLp;9`ox;wupyenjx8*tf+@B~ z=cu5#n{=@Ww{IbR@FHwt2wnPJ&KOWny@}+0|K7Mti(cW73i8YHcO4sVVii$$H$=)+ ziYMzlF#~Z_;%3KTN-Z6t@P1OIUssN^y;s+J;%{;mAn;j@ND|D)kpQHd*RbQYhPYr4~g`- z7{JK)z0Zn?8CG5S=Uka)P^))5Tq=-ib=3n^BL=+f6m4MR<9k!0Vdvum(UB&WQU4NP zM*}aA6Pt|mK6!)jzOkm}{6E$qoId#y4LZ8M$<+Yg>hsyZw$D$R<;0TT#pYF`H(C+E z9wwW@gM>Sv$L4oAcBZHIUF_*<8vGJh!~{YiKwb9Y5j{vsG8a=N`6*^WwTvS~))X;cymr7_c5|{?W@k`2`2QSZ1WI-|*%7tXGR3 z4&G-=QsN{3>_Wd%8dp%Yw-Uax?R5a^P2~wWzbef;IKYe`hTKMg_dvgJqg}_L*in6L zUWsR`Z8_W~)$@#uj7Qf(Hby$Bj;g9WU*n~FzeZwzNbSrLIU{caum)Gj?r6~>&U91DX@M68qx`_J{pR(~hkd&C8{BPh(Xsug`#WE0K z`z&1qx-jCv5Dw=ZCn%$o0+Gpn1hN3s@w^J@@##HGmxd#niUU5hZ+=uPG&<}PsfzfH zy$4EJ)IRPZ!+7uXl6itr65Oc1R5uB*lI9ScGZlbcACqV1=cB;M(n%WsL0@UcbHy@R zTu9?Av9urztLMuY%UEYCCd|NSJ#y9|=c7Q(LN7fndZ_S=-fV+3EgmCgUK!N%+b995 z`OO-z&urza-_4a|n6~;)eCnHw8z?$zjlWQnM}Dc8vIT{K6Nk_w;)k}9b1mXs@q}@B z|6>M9vB95gtO?s@N-SvxENRT_snKCz3C-zgT_e{LEB(o%VqtLjDZzxu({X6{LwMC# z(;F8KPldvXT60Kzp)H@ifh^NEm+`O#no&n}ZIUY=@FRd4N>E=4Sk7w|#KNJXqN4o# z=(f}bOOaqDJf+-_uqvP&m`+KekKxAZBMnWqgrLXE>7;@OnSMkx)-a4DpDYxtqe|@j zSY1@q)}C$v#-YV%FSoLSMIjk+^jQ|RHFM0~+)f^wBDlX*$4B`Zb@TkaZa(y4DW_~r z#O1u8BC-huty+FKY@jGLwumj3)?OE#qXld5hI`IrdU#kt9ZWPVt%r?4();)PzX?50 z0_N7Acbyl+sD&w)W?|9pPx=#=kV`IxyMR1!vOGPAhU@JpexsRQoSgxvaBXePzJxSx zpkSbqUZF%I#vO;DMxz`^Cuy;~8*v-4y|%3HObO#A)rEfo(Ey$_m-6LIxn`3SW6Cg~ z27gt5!oP@hhpX{qc^Fq5-TS)1L+ISb<>cfFIxp%A`>UzQ{JW!OsK4QLj$B}T#Ol${xeO=|9VHSb9Vi>1{}94EoL&{M*8_!=qsh23twV%9#Q z&-_2gg|Y047Y9zUHcJ}UU|*jUq}fw^-%w|mnL{DUY&>auAx64x(GlrKKM){fB*p@a ze)qvBxk%3rnrP{yG+1@Jm)XR-Ht#ciM~~KIB%KARcpi`k0IqebwCIxlbejbO^HSrv zwG>HQ&}Aut_d#Jux%eg5-FP0zXm0-G8pcd@bU6!;T65%69yvZb3jfUMp>*T7x%Teh zi&ujTGk|_eG%%wRYW04YR7+u|`$b!e`}D#HDF`eTw4mZO{y9JmurFM^23Z1SY2UDsa`2 zANkP5c%BPq{u&5Q^>+iM_*VrCITWXal_9~h)Vmn|KCZ0`pW-Q`LPAN$<%FT_QaiuA zl#yM?u4vX{cXvps-T(GYHnuQVa^eRQ^Y)FBgC~Xt#tmnH*(dC_bGI1q{P_7o$n2~_0In3W-eJ|IvHw~2mDV)> z8fmF|rTup!4yk!LN%=;D2#%G6ZLLaeNEY)5VWyA-*z5C|-(HqYn5yKQ@Lwyz6hikw zX1RDehLPX1QPo@VV*h?^N3{5kgL_bHqSX@!%u_Kwj*ef77G$7G6r?s?d`!?42_PpF zZxcQs^?uq4b`mg9PqlAEXL1BmXG<%R95f7mUDFOqxam zWiUu-o`W9m zXQ)KM+h1(ig!uThD(6gmcS+R2n~wK;uA=XHzN+b**9#c-7aL8WWsmOBW#Em%2_j9< z-qNzgO4CmIpdGHWkAzW5_sm93J^{E=c*uLtEpYuV>gr=Jg}7}r-{qt;ctkeNlPRe${jP9w6(SMQRC9{s|W7QR`WIY`HuYCTbn^y z*$d0f&l^yq^|$b>@j^9#{z>T~4OPFP^{kQ4uSwu8jUS&Fn;0GU%MG%$0(BOE z+vRhKAvdEufWD|m5L0t@wJ@2V{J8cNn2XtVUb}p1!yE)O)2x5+J}Depoa!ze4Z(_F zNmJx|b15?K>Zq?15;;JCR$%AmX8+0S)6n?amS+mtXjmlJ&GBD{@Y&P!%XwPks+yu* zixxk7eODJ1F)`C05kk%YL}<4U{b6E1gs7deINS!c%2V` zaix+ z@UY3=8p~CNQ4d68)E!eO=Y2gM{x>>C67;z3|FW(7Oz)GUSmJj-Hhm#2LYZjuJ1gyB zXS+bOn?hZ(2DUv!p*hr^6J4C;h&AK_tVLKd&9cP!GEH}SaNkklBW0%AX zEBqPifDJ*uJp?L57ya?&pl{>fVQ{8x#xj`UQxaPmKT{cieE?XEfj?!FEff7_U@nR} z`!O6JfQZiwtl!zDhkyZjCk5c!5Sj^Cu3V_-8>giRY$UkYGBrf*L z|D>YAKaN5eQ6Eldm!J*>jH>=TKlXux%#w#0=vIgYV$ARj*;Tx9n#XO^HFi)@MP!2u zln{?73|0$afb%Y;g?&i0|3?l%BO~M*!W?w8;>mh1@?HC<5qLfSLm;~ATE@ur^)<@M zAI-XXObj%C9U8dNe4`UU=Jl#{%J+NXr4*8Z;4&~SfGzh11%<}Qv}(pQ;NqPGbyT(B z5BQu8IXEHD!3m zEl`b|!cig-h5c8z*-wPSAK5As&J0DwksG`Kp+_vEt?lpczkQ+irS{N&=Xn+Bm{`U( zUk+mPHklWnzX8AXZ+vaQ2TqWr&{w=VJK@PO80}z3nF^k%t zHEMrF5_x3{x~O6CyppKnEleP3FugkJ(nmJRS3xQ+)45{vxB+V5Z`7cXG%mJ38aDkn zQH{2IO_7P++`YswkE;?Q;i#J?Ve7ltOx^@cS&FpElGllyw+q{eyKaI0|4t9btbQM8 zowJ4)&PAf+ZpgP<*DHXKP0F2?KYoW+f~ar*_N4n$4_LLK()6>B%<(mD=Ts%2Z<&z@ znK$r$3pay@aX6C4P6DeZnS^GVuW)>Rg5$@U5GIh5mK-wmh5ZZ;m_1sOvKGwG$q^2` zpNW}I0Ae8VpFe+2OuR|__py!%qzA?{_}lBlN>TtQ5w3WpQbuBp^fJ_OUi*QvB9OGv zieIQ`+jD|`&|0bn{^mPT35gud#IOraVyx^68Q2u=KD!Wkr4b_6cYEa!1*9}9745zw zn4rm#F_9~Hd3-4c-aPXe`YgKGpo&;$u~<>RssZSM5`x_X#_-5Z^j&p_@$L$5*JQ@in z9G6E>QM-+TK9KAH#Ku~mAL^1!FX_*L#5|$Wem53;I~Vogs%Nc}jV8JnJ5A<564)=S z>&tI$_SFjF+jyJOdC|iOU88=S89t|7ndY31daiN-!9@9rivN6bNV}d=J}#w4n#X5_ zk24}o^KFX!SLX--J-dzXY>l5AbSeX`tGn*~gKx@XJN3=8aXeAyKz>7fF>+{IAyoxrFZz zxmqIaXktF}Uw1fuz}0#HD(rdrwRlEvZhn5VlQ8M$p>5ZE%ng6UwO>cM!0!W(HenoR zjKO4jiRo$V^#ClWGo0IN$orHb-~dA?ge=lLn?X-(5MnlvZY5!?uO^IHL=D;s4R_vb z7~3|&ZdnMLoq4bLt-0CT#|KIpCy_M+P^K87>a0>su{0|?Tifrj(Z~LD4~d4c|L{k- zBy%@y@`En!9oAI}ET9mRJGwA5i4f^b#1TMZam)lN)({y|Eh`)0_#aR$mYI-zLy!5j zG=NSWY!qZ2B&DC?$S7yAxOW`qNiv{f%`%lE+@s0asEGBKr*+OWMJ{!s?^ktfD7Y?QUCv1&N@CR+)1QDcfj#N)aZDi>URN&8r@kt&NMp zWlcB5nLI;pHkRtE&wBd% zt6Zi_9~~c*Roibn zZ`xlb3#CrV1Ht_{rOXP1#8X+N`b@+?W<{}tPdR)VX@hofA>Y2$sLj<@KZ;wn-=Vps zs488(QkyguJ3?}EXE+6Wx8*ssv$nQ2G_=RjIa?l-y~#6J7lI9eGB-GfQbzMYn^||Ow)Sitn~b{A{MY<^UG3SX6FoNcnKzk#>x1}@ ze$z50a5ufJH!@)&-kSh&M34R43Je%{6c949Vawk*wKA~iX3uXFy+E&?CPsGC$=jsH zDS|Le7{p*pQxTcL?X51L!D@}zb^q6ux14UppDk4KgEI%{=SqMV#}>&a6Jr8?od+AK zv+i2qyE1SaV%fftHc7sCDkPhaDW4Cg4}+QJ0B1U1Wa8-T%#3(5c(c3I1mG3V$J zhN7rrb`!u<{5o{Xbz^rQjVS5rf?q;wr!IPWdZcL)_St8Yjss4{6nCGpOV>xR5*W39 z_VIcyE^nYx%zFYGd?kQ?%QTq9t(!E=Lxr>n6SmtQ|4spk21crf+pd@aI#8{dVcsPc z^_QxF75vM6YwW&Ped$(qn>C1|+(H+qQI(FTarOLR#NC~wIYOiSKMkLPV6Kz{AUIgG z=@QIAKG$1OVIc{nV;xUiiACx@7}^L4lcR`{E`XATNvRWur+jSXcLdiVv{)Jqa0h=d zrYJ-^JbrhRQeA8joR}DUuI_pQx zNEtnh%$wK;3XsbP3lUOhzBdp5jAxdY*XNfrwnUFfC4w;NH?T}3kHqy|2vc#d~I2a^y8wya3DO+)+Xzo+VmANkWK+4#o))I~K zS$hIWo-7=u?<7PimEnC9=KV~82Y)__z3i&``%Q0eZ)YjVrb>@5e{RfHUiB0msl)C2 zK7Sbbi5C95d=v;70XeF`z!ohY;DbWHk1_#$yyz=#<0Sg%3G)`C&EI4Q7vVUqCRlt; z=8#>t#Y;eG0a4U2N|AkvS4{{etWhmssUsCSVzZ#om;kX2oN#x9oq^ETe|E)3-@3y6 zM)xlPp$#NLBB%OM%cjdcSV-{XNMwK)O){b#7Pc*r_Yi3J1PD#GH@$wmxDk#Q0$Tq< zz5!oMJ53N3IkqUX6o~Y*jVw3b_rQ>Y-z(Q^l"* ]]; then + echo -e "\nERROR: Some error ocurred when submitting" + exit 1 + fi + done < <(echo "$row" | jq -c '.content[]') +done < <(jq -c '.[]' $1) + + +echo +echo +echo +echo "All models submitted correctly!" \ No newline at end of file diff --git a/selfapi/.gitignore b/selfapi/.gitignore new file mode 100644 index 0000000..daf913b --- /dev/null +++ b/selfapi/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/selfapi/.swagger-codegen-ignore b/selfapi/.swagger-codegen-ignore new file mode 100644 index 0000000..c5fa491 --- /dev/null +++ b/selfapi/.swagger-codegen-ignore @@ -0,0 +1,23 @@ +# Swagger Codegen Ignore +# Generated by swagger-codegen https://github.com/swagger-api/swagger-codegen + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell Swagger Codgen to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/selfapi/.swagger-codegen/VERSION b/selfapi/.swagger-codegen/VERSION new file mode 100644 index 0000000..6381ae0 --- /dev/null +++ b/selfapi/.swagger-codegen/VERSION @@ -0,0 +1 @@ +2.4.18 \ No newline at end of file diff --git a/selfapi/.travis.yml b/selfapi/.travis.yml new file mode 100644 index 0000000..f5cb2ce --- /dev/null +++ b/selfapi/.travis.yml @@ -0,0 +1,8 @@ +language: go + +install: + - go get -d -v . + +script: + - go build -v ./ + diff --git a/selfapi/README.md b/selfapi/README.md new file mode 100644 index 0000000..f7b9284 --- /dev/null +++ b/selfapi/README.md @@ -0,0 +1,100 @@ +# Go API client for swagger + +Backend of the oc-search project + +## Overview +This API client was generated by the [swagger-codegen](https://github.com/swagger-api/swagger-codegen) project. By using the [swagger-spec](https://github.com/swagger-api/swagger-spec) from a remote server, you can easily generate an API client. + +- API version: 1.0.0 +- Package version: 1.0.0 +- Build package: io.swagger.codegen.languages.GoClientCodegen + +## Installation +Put the package under your project folder and add the following in import: +```golang +import "./swagger" +``` + +## Documentation for API Endpoints + +All URIs are relative to *https://localhost:49618/v1* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +*ComputingApi* | [**ComputingControllerAddComputing**](docs/ComputingApi.md#computingcontrolleraddcomputing) | **Post** /computing/ | +*ComputingApi* | [**ComputingControllerGetComputingByID**](docs/ComputingApi.md#computingcontrollergetcomputingbyid) | **Get** /computing/{ID} | +*ComputingApi* | [**ComputingControllerGetMultipleComputingByIDs**](docs/ComputingApi.md#computingcontrollergetmultiplecomputingbyids) | **Get** /computing/multi/{IDs} | +*DataApi* | [**DataControllerCreateData**](docs/DataApi.md#datacontrollercreatedata) | **Post** /data/ | +*DataApi* | [**DataControllerGetDataByID**](docs/DataApi.md#datacontrollergetdatabyid) | **Get** /data/{ID} | +*DataApi* | [**DataControllerGetMultipleDataByIDs**](docs/DataApi.md#datacontrollergetmultipledatabyids) | **Get** /data/multi/{IDs} | +*DatacenterApi* | [**DatacenterControllerCreateDatacenter**](docs/DatacenterApi.md#datacentercontrollercreatedatacenter) | **Post** /datacenter/ | +*DatacenterApi* | [**DatacenterControllerGetMultipleDatacentersByIDs**](docs/DatacenterApi.md#datacentercontrollergetmultipledatacentersbyids) | **Get** /datacenter/multi/{IDs} | +*DatacenterApi* | [**DatacenterControllerGetOneDatacenter**](docs/DatacenterApi.md#datacentercontrollergetonedatacenter) | **Get** /datacenter/{ID} | +*ScheduleApi* | [**ScheduleControllerCheckIfScheduleCanBeCreatedInThisDC**](docs/ScheduleApi.md#schedulecontrollercheckifschedulecanbecreatedinthisdc) | **Post** /schedule/check | +*ScheduleApi* | [**ScheduleControllerCreateSchedule**](docs/ScheduleApi.md#schedulecontrollercreateschedule) | **Post** /schedule/book | +*ScheduleApi* | [**ScheduleControllerGetNextSchedule**](docs/ScheduleApi.md#schedulecontrollergetnextschedule) | **Get** /schedule/next | +*ScheduleApi* | [**ScheduleControllerGetPreviousSchedule**](docs/ScheduleApi.md#schedulecontrollergetpreviousschedule) | **Get** /schedule/previous | +*ScheduleApi* | [**ScheduleControllerGetSchedules**](docs/ScheduleApi.md#schedulecontrollergetschedules) | **Get** /schedule/ | +*SearchApi* | [**SearchControllerSearchByWord**](docs/SearchApi.md#searchcontrollersearchbyword) | **Get** /search/byWord | +*StorageApi* | [**StorageControllerCreateStorage**](docs/StorageApi.md#storagecontrollercreatestorage) | **Post** /storage/ | +*StorageApi* | [**StorageControllerGet**](docs/StorageApi.md#storagecontrollerget) | **Get** /storage/{ID} | +*StorageApi* | [**StorageControllerGetMultipleStoragesByIDs**](docs/StorageApi.md#storagecontrollergetmultiplestoragesbyids) | **Get** /storage/multi/{IDs} | +*UserApi* | [**UserControllerLogin**](docs/UserApi.md#usercontrollerlogin) | **Get** /user/login | +*UserApi* | [**UserControllerLogout**](docs/UserApi.md#usercontrollerlogout) | **Get** /user/logout | +*WorkflowApi* | [**WorkflowControllerAddNewObjectToAWorkflow**](docs/WorkflowApi.md#workflowcontrolleraddnewobjecttoaworkflow) | **Post** /workflow/{workflowName}/add | +*WorkflowApi* | [**WorkflowControllerBookSchedule**](docs/WorkflowApi.md#workflowcontrollerbookschedule) | **Post** /workflow/{workflowName}/schedule/book | +*WorkflowApi* | [**WorkflowControllerCheckSchedule**](docs/WorkflowApi.md#workflowcontrollercheckschedule) | **Get** /workflow/{workflowName}/schedule/check | +*WorkflowApi* | [**WorkflowControllerCreateANewWorkflow**](docs/WorkflowApi.md#workflowcontrollercreateanewworkflow) | **Post** /workflow/ | +*WorkflowApi* | [**WorkflowControllerCreateARealtionshipBetweenTwoRobjects**](docs/WorkflowApi.md#workflowcontrollercreatearealtionshipbetweentworobjects) | **Post** /workflow/{workflowName}/link | +*WorkflowApi* | [**WorkflowControllerGetMxGraphLastStatus**](docs/WorkflowApi.md#workflowcontrollergetmxgraphlaststatus) | **Get** /workflow/{workflowName}/mxGraphParser | +*WorkflowApi* | [**WorkflowControllerGetSchedule**](docs/WorkflowApi.md#workflowcontrollergetschedule) | **Get** /workflow/{workflowName}/schedule | +*WorkflowApi* | [**WorkflowControllerGetWorkflow**](docs/WorkflowApi.md#workflowcontrollergetworkflow) | **Get** /workflow/{workflowName} | +*WorkflowApi* | [**WorkflowControllerListWorkflows**](docs/WorkflowApi.md#workflowcontrollerlistworkflows) | **Get** /workflow/ | +*WorkflowApi* | [**WorkflowControllerParseMxGraph**](docs/WorkflowApi.md#workflowcontrollerparsemxgraph) | **Post** /workflow/{workflowName}/mxGraphParser | +*WorkflowApi* | [**WorkflowControllerSetSchedule**](docs/WorkflowApi.md#workflowcontrollersetschedule) | **Put** /workflow/{workflowName}/schedule | +*WorkspaceApi* | [**WorkspaceControllerAddModelToWorkspace**](docs/WorkspaceApi.md#workspacecontrolleraddmodeltoworkspace) | **Post** /workspace/ | +*WorkspaceApi* | [**WorkspaceControllerDeleteElementFromUserWorkspace**](docs/WorkspaceApi.md#workspacecontrollerdeleteelementfromuserworkspace) | **Delete** /workspace/ | +*WorkspaceApi* | [**WorkspaceControllerGetFullWorkspace**](docs/WorkspaceApi.md#workspacecontrollergetfullworkspace) | **Get** /workspace/list_model | +*WorkspaceApi* | [**WorkspaceControllerGetWorkspace**](docs/WorkspaceApi.md#workspacecontrollergetworkspace) | **Get** /workspace/list | + + +## Documentation For Models + + - [ModelsComputingModel](docs/ModelsComputingModel.md) + - [ModelsComputingNewModel](docs/ModelsComputingNewModel.md) + - [ModelsComputingObject](docs/ModelsComputingObject.md) + - [ModelsDCstatus](docs/ModelsDCstatus.md) + - [ModelsDataModel](docs/ModelsDataModel.md) + - [ModelsDataNewModel](docs/ModelsDataNewModel.md) + - [ModelsDataObject](docs/ModelsDataObject.md) + - [ModelsDatacenterCpuModel](docs/ModelsDatacenterCpuModel.md) + - [ModelsDatacenterGpuModel](docs/ModelsDatacenterGpuModel.md) + - [ModelsDatacenterMemoryModel](docs/ModelsDatacenterMemoryModel.md) + - [ModelsDatacenterModel](docs/ModelsDatacenterModel.md) + - [ModelsDatacenterNewModel](docs/ModelsDatacenterNewModel.md) + - [ModelsDatacenterObject](docs/ModelsDatacenterObject.md) + - [ModelsExecutionRequirementsModel](docs/ModelsExecutionRequirementsModel.md) + - [ModelsRepositoryModel](docs/ModelsRepositoryModel.md) + - [ModelsScheduleDb](docs/ModelsScheduleDb.md) + - [ModelsScheduleInfo](docs/ModelsScheduleInfo.md) + - [ModelsScheduleTime](docs/ModelsScheduleTime.md) + - [ModelsSearchResult](docs/ModelsSearchResult.md) + - [ModelsStorageModel](docs/ModelsStorageModel.md) + - [ModelsStorageNewModel](docs/ModelsStorageNewModel.md) + - [ModelsStorageObject](docs/ModelsStorageObject.md) + - [ModelsWorkflow](docs/ModelsWorkflow.md) + - [ModelsWorkflowSchedule](docs/ModelsWorkflowSchedule.md) + - [ModelsWorkspace](docs/ModelsWorkspace.md) + - [ModelsWorkspaceModel](docs/ModelsWorkspaceModel.md) + - [PrimitiveObjectId](docs/PrimitiveObjectId.md) + - [TimeTime](docs/TimeTime.md) + + +## Documentation For Authorization + Endpoints do not require authorization. + + +## Author + +opencloud@irt-saintexupery.com + diff --git a/selfapi/api/swagger.yaml b/selfapi/api/swagger.yaml new file mode 100644 index 0000000..17dbfb1 --- /dev/null +++ b/selfapi/api/swagger.yaml @@ -0,0 +1,1996 @@ +--- +swagger: "2.0" +info: + description: "Backend of the oc-search project" + version: "1.0.0" + title: "oc-catalog API" + contact: + email: "opencloud@irt-saintexupery.com" +host: "localhost:49618" +basePath: "/v1" +tags: +- name: "data" + description: "All operations related to the rType data\n" +- name: "computing" + description: "All operations related to the rType computing\n" +- name: "datacenter" + description: "DatacenterController operations about datacenters\n" +- name: "storage" + description: "StorageController operations about storage\n" +paths: + /computing/: + post: + tags: + - "computing" + description: "Submit a computing object" + operationId: "ComputingController.Add computing" + parameters: + - in: "body" + name: "body" + description: "The object content" + required: true + schema: + $ref: "#/definitions/models.ComputingNEWModel" + x-exportParamName: "Body" + responses: + "200": + description: "{string} ID" + "403": + description: "Missing body or fields" + /computing/multi/{IDs}: + get: + tags: + - "computing" + description: "Return Computing objects if found in the DB. Not found IDs will\ + \ be ignored" + operationId: "ComputingController.Get multiple computing by IDs" + parameters: + - name: "IDs" + in: "path" + description: "List of computing IDs" + required: true + type: "array" + items: + type: "string" + x-exportParamName: "IDs" + responses: + "200": + description: "" + schema: + type: "array" + items: + $ref: "#/definitions/models.ComputingModel" + "403": + description: "IDs are empty" + /computing/{ID}: + get: + tags: + - "computing" + description: "Find a computing resource based on ID" + operationId: "ComputingController.Get computing by ID" + parameters: + - name: "ID" + in: "path" + description: "The ID of the resource" + required: true + type: "string" + x-exportParamName: "ID" + responses: + "200": + description: "" + schema: + $ref: "#/definitions/models.ComputingModel" + "403": + description: "ID is empty" + /data/: + post: + tags: + - "data" + description: "Submit data object" + operationId: "DataController.Create Data" + parameters: + - in: "body" + name: "body" + description: "The object content" + required: true + schema: + $ref: "#/definitions/models.DataNEWModel" + x-exportParamName: "Body" + responses: + "200": + description: "{string} ID" + "403": + description: "Missing body or fields" + /data/multi/{IDs}: + get: + tags: + - "data" + description: "Return Data object if found in the DB. Not found IDs will be ignored" + operationId: "DataController.Get multiple data by IDs" + parameters: + - name: "IDs" + in: "path" + description: "List of data IDs" + required: true + type: "array" + items: + type: "string" + x-exportParamName: "IDs" + responses: + "200": + description: "" + schema: + type: "array" + items: + $ref: "#/definitions/models.DataModel" + "403": + description: "IDs are empty" + /data/{ID}: + get: + tags: + - "data" + description: "Find rType data based on ID" + operationId: "DataController.Get data by ID" + parameters: + - name: "ID" + in: "path" + description: "The ID of the data resource" + required: true + type: "string" + x-exportParamName: "ID" + responses: + "200": + description: "" + schema: + $ref: "#/definitions/models.DataModel" + "403": + description: "ID is empty" + /datacenter/: + post: + tags: + - "datacenter" + description: "submit Datacenter object" + operationId: "DatacenterController.Create Datacenter" + parameters: + - in: "body" + name: "body" + description: "The object content" + required: true + schema: + $ref: "#/definitions/models.DatacenterNEWModel" + x-exportParamName: "Body" + responses: + "200": + description: "{string} models.DatacenterModel" + "403": + description: "Missing body or fields" + /datacenter/multi/{IDs}: + get: + tags: + - "datacenter" + description: "Return Datacenter objects if found in the DB. Not found IDs will\ + \ be ignored" + operationId: "DatacenterController.Get multiple datacenters by IDs" + parameters: + - name: "IDs" + in: "path" + description: "List of datacenter IDs" + required: true + type: "array" + items: + type: "string" + x-exportParamName: "IDs" + responses: + "200": + description: "" + schema: + type: "array" + items: + $ref: "#/definitions/models.ComputingModel" + "403": + description: "IDs are empty" + /datacenter/{ID}: + get: + tags: + - "datacenter" + description: "find datacenter by ID" + operationId: "DatacenterController.GetOneDatacenter" + parameters: + - name: "ID" + in: "path" + description: "the ID you want to get" + required: true + type: "string" + x-exportParamName: "ID" + responses: + "200": + description: "" + schema: + $ref: "#/definitions/models.DatacenterModel" + "403": + description: "ID is empty" + /schedule/: + get: + tags: + - "schedule" + description: "Get a list of next startDates schedules (inclusive). If timezone\ + \ is not specified, will assume UTC" + operationId: "ScheduleController.Get schedules" + parameters: + - name: "startDate" + in: "query" + description: "Start date" + required: true + x-exportParamName: "StartDate" + - name: "stopDate" + in: "query" + description: "End date" + required: true + x-exportParamName: "StopDate" + responses: + "200": + description: "" + schema: + type: "array" + items: + $ref: "#/definitions/models.ScheduleDB" + "201": + description: "Too much elements within the range of dates" + "400": + description: "Other error. Check the output" + "403": + description: "Authentication issue" + /schedule/book: + post: + tags: + - "schedule" + description: "Create schedule for a workflow. It will return some future executions\ + \ just as information" + operationId: "ScheduleController.Create schedule" + parameters: + - name: "dcName" + in: "query" + description: "Name of the node (oc-catalog) from where the workflow comes." + required: true + type: "string" + x-exportParamName: "DcName" + - name: "workflowName" + in: "query" + description: "Workflow Name" + required: true + type: "string" + x-exportParamName: "WorkflowName" + - name: "cron" + in: "query" + description: "Cron syntax with year. If no year is specified, will use the\ + \ current" + required: true + type: "string" + x-exportParamName: "Cron" + - name: "duration" + in: "query" + description: "Duration in seconds" + required: true + type: "integer" + format: "int32" + x-exportParamName: "Duration" + - name: "startDate" + in: "query" + description: "RFC3339 time for startDate" + required: true + x-exportParamName: "StartDate" + - name: "stopDate" + in: "query" + description: "RFC3339 time for stopDate" + required: true + x-exportParamName: "StopDate" + - in: "body" + name: "requirements" + description: "The object content" + required: true + schema: + $ref: "#/definitions/models.ExecutionRequirementsModel" + x-exportParamName: "Requirements" + responses: + "200": + description: "" + schema: + $ref: "#/definitions/models.ScheduleInfo" + "400": + description: "workflowName not found or empty" + "403": + description: "Authentication issue" + /schedule/check: + post: + tags: + - "schedule" + description: "Check for availability of this DC" + operationId: "ScheduleController.Check if schedule can be created in this DC" + parameters: + - name: "cron" + in: "query" + description: "Cron syntax" + required: true + type: "string" + x-exportParamName: "Cron" + - name: "duration" + in: "query" + description: "Duration in seconds" + required: true + type: "integer" + format: "int32" + x-exportParamName: "Duration" + - name: "startDate" + in: "query" + description: "RFC3339 time for startDate" + required: true + x-exportParamName: "StartDate" + - name: "stopDate" + in: "query" + description: "RFC3339 time for stopDate" + required: true + x-exportParamName: "StopDate" + - in: "body" + name: "requirements" + description: "The object content" + required: true + schema: + $ref: "#/definitions/models.ExecutionRequirementsModel" + x-exportParamName: "Requirements" + responses: + "200": + description: "The schedule can be created" + "400": + description: "Other error. Check the output" + "403": + description: "Authentication issue" + /schedule/next: + get: + tags: + - "schedule" + description: "Give a date, get the next date where there are at least on schedule.\ + \ If no hours specified, will assume 00:00" + operationId: "ScheduleController.Get next schedule" + parameters: + - name: "baseDate" + in: "query" + description: "Base date" + required: true + x-exportParamName: "BaseDate" + responses: + "200": + description: "" + schema: + $ref: "#/definitions/*time.Time" + "400": + description: "Other error. Check the output" + "403": + description: "Authentication issue" + /schedule/previous: + get: + tags: + - "schedule" + description: "Give a date, get the previous date where there are at least on\ + \ schedule. If no hours specified, will assume 00:00" + operationId: "ScheduleController.Get previous schedule" + parameters: + - name: "baseDate" + in: "query" + description: "Base date" + required: true + x-exportParamName: "BaseDate" + responses: + "200": + description: "" + schema: + $ref: "#/definitions/*time.Time" + "400": + description: "Other error. Check the output" + "403": + description: "Authentication issue" + /search/byWord: + get: + tags: + - "search" + description: "find resources by word" + operationId: "SearchController.Search by word" + parameters: + - name: "word" + in: "query" + description: "Word to search across all resources" + required: true + type: "string" + x-exportParamName: "Word" + responses: + "200": + description: "" + schema: + $ref: "#/definitions/models.SearchResult" + "503": + description: "Internal error" + /storage/: + post: + tags: + - "storage" + description: "submit storage object" + operationId: "StorageController.Create Storage" + parameters: + - in: "body" + name: "body" + description: "The object content" + required: true + schema: + $ref: "#/definitions/models.StorageNEWModel" + x-exportParamName: "Body" + responses: + "200": + description: "{string} models.StorageModel" + "403": + description: "Missing body or fields" + /storage/multi/{IDs}: + get: + tags: + - "storage" + description: "Return Storage objects if found in the DB. Not found IDs will\ + \ be ignored" + operationId: "StorageController.Get multiple storages by IDs" + parameters: + - name: "IDs" + in: "path" + description: "List of storage IDs" + required: true + type: "array" + items: + type: "string" + x-exportParamName: "IDs" + responses: + "200": + description: "" + schema: + type: "array" + items: + $ref: "#/definitions/models.ComputingModel" + "403": + description: "IDs are empty" + /storage/{ID}: + get: + tags: + - "storage" + description: "find storage by ID" + operationId: "StorageController.Get" + parameters: + - name: "ID" + in: "path" + description: "the ID you want to get" + required: true + type: "string" + x-exportParamName: "ID" + responses: + "200": + description: "" + schema: + $ref: "#/definitions/models.StorageModel" + "403": + description: "ID is empty" + /user/login: + get: + tags: + - "user" + description: "Logs user into the system" + operationId: "UserController.Login" + parameters: + - name: "username" + in: "query" + description: "The username for login" + required: true + type: "string" + x-exportParamName: "Username" + - name: "password" + in: "query" + description: "The password for login" + required: true + type: "string" + x-exportParamName: "Password" + responses: + "200": + description: "{string} login success" + "403": + description: "user not exist" + /user/logout: + get: + tags: + - "user" + description: "Logs out current logged in user session" + operationId: "UserController.logout" + parameters: [] + responses: + "200": + description: "{string} logout success" + /workflow/: + get: + tags: + - "workflow" + description: "List available workflows" + operationId: "WorkflowController.List workflows" + parameters: [] + responses: + "200": + description: "[]string List of workflows" + "400": + description: "{string} Other error" + "403": + description: "Authentication issue" + post: + tags: + - "workflow" + description: "Create a name for the new workflow" + operationId: "WorkflowController.Create a new workflow" + parameters: + - name: "workflowName" + in: "query" + description: "Name of the workflow" + required: true + type: "string" + x-exportParamName: "WorkflowName" + responses: + "200": + description: "{string} Workflow created succeful" + "400": + description: "{string} Other error" + "403": + description: "Authentication issue" + /workflow/{workflowName}: + get: + tags: + - "workflow" + description: "Get a workflow by name" + operationId: "WorkflowController.Get Workflow" + parameters: + - name: "workflowName" + in: "path" + description: "Workflow Name" + required: true + type: "string" + x-exportParamName: "WorkflowName" + responses: + "200": + description: "" + schema: + $ref: "#/definitions/models.Workflow" + "400": + description: "{string} Other error" + "403": + description: "Authentication issue" + /workflow/{workflowName}/add: + post: + tags: + - "workflow" + description: "Create a Rtype object from already added resources to the workspace" + operationId: "WorkflowController.Add new object to a Workflow" + parameters: + - name: "workflowName" + in: "path" + description: "workflow Name" + required: true + type: "string" + x-exportParamName: "WorkflowName" + - name: "rID" + in: "query" + description: "rID of already existing item in Workspace" + required: true + type: "string" + x-exportParamName: "RID" + responses: + "200": + description: "{string} ID of the new object (rObjID)" + "400": + description: "{string} Other error" + "403": + description: "Authentication issue" + /workflow/{workflowName}/link: + post: + tags: + - "workflow" + description: "Create a Rtype object from already added resources to the workspace" + operationId: "WorkflowController.Create a realtionship between two Robjects" + parameters: + - name: "workflowName" + in: "path" + description: "Workflow Name" + required: true + type: "string" + x-exportParamName: "WorkflowName" + - name: "rObjIDsource" + in: "query" + description: "Robject source. Usually Data" + required: true + type: "string" + x-exportParamName: "RObjIDsource" + - name: "isInput" + in: "query" + description: "If the operation is for input (true) linkage or output (false)" + required: true + type: "boolean" + x-exportParamName: "IsInput" + - name: "rObjIDtarger" + in: "query" + description: "Robject where will be written the association" + required: true + type: "string" + x-exportParamName: "RObjIDtarger" + responses: + "200": + description: "{string} ID of the new object (rObjID)" + "400": + description: "{string} Other error" + "403": + description: "Authentication issue" + /workflow/{workflowName}/mxGraphParser: + get: + tags: + - "workflow" + description: "Obtain the last mxgraph XML status from the workflow" + operationId: "WorkflowController.Get mxGraph last status" + parameters: + - name: "workflowName" + in: "path" + description: "Workflow Name" + required: true + type: "string" + x-exportParamName: "WorkflowName" + responses: + "200": + description: "The xmlgraph" + "201": + description: "Empty workflow" + "400": + description: "{string} Other error" + "403": + description: "Authentication issue" + post: + tags: + - "workflow" + description: "If we use this aproach to transofrm mxgraph representation in\ + \ our representation, we should not use other API calls for modify the project\ + \ structure or we'll have inconsistencies." + operationId: "WorkflowController.Parse mxGraph" + parameters: + - name: "workflowName" + in: "path" + description: "Workflow Name" + required: true + type: "string" + x-exportParamName: "WorkflowName" + - in: "body" + name: "xmlData" + description: "Xml representation of the workflow" + required: true + schema: + type: "string" + x-exportParamName: "XmlData" + responses: + "200": + description: "The xmlgraph consumed correctly" + "201": + description: "The xmlgraph consumed with issues" + "400": + description: "{string} Other error" + "403": + description: "Authentication issue" + /workflow/{workflowName}/schedule: + get: + tags: + - "workflow" + description: "Obtain the desired schedule of this workflow" + operationId: "WorkflowController.Get Schedule" + parameters: + - name: "workflowName" + in: "path" + description: "Workflow Name" + required: true + type: "string" + x-exportParamName: "WorkflowName" + responses: + "200": + description: "" + schema: + $ref: "#/definitions/models.ScheduleTime" + "400": + description: "Workflow doesn't exist" + "401": + description: "Other error" + "403": + description: "Authentication issue" + put: + tags: + - "workflow" + description: "Set desired schedule by the user. No other effects a part of saving\ + \ the user input" + operationId: "WorkflowController.Set Schedule" + parameters: + - name: "workflowName" + in: "path" + description: "Workflow Name" + required: true + type: "string" + x-exportParamName: "WorkflowName" + - name: "isService" + in: "query" + description: "True: Service, False: Task" + required: true + type: "boolean" + x-exportParamName: "IsService" + - name: "startDate" + in: "query" + description: "RFC3339 time for startDate" + required: true + x-exportParamName: "StartDate" + - name: "stopDate" + in: "query" + description: "RFC3339 time for stopDate" + required: true + x-exportParamName: "StopDate" + - name: "events" + in: "query" + description: "List of events separated by comma" + required: false + type: "string" + x-exportParamName: "Events" + x-optionalDataType: "String" + - name: "cronString" + in: "query" + description: "Cron string" + required: false + type: "string" + x-exportParamName: "CronString" + x-optionalDataType: "String" + - name: "duration" + in: "query" + description: "Duration in seconds" + required: false + type: "integer" + format: "int32" + x-exportParamName: "Duration" + x-optionalDataType: "Int32" + responses: + "200": + description: "" + schema: + $ref: "#/definitions/models.ScheduleInfo" + "400": + description: "Workflow doesn't exist" + "401": + description: "Other error" + "402": + description: "Bad user input" + "403": + description: "Authentication issue" + /workflow/{workflowName}/schedule/book: + post: + tags: + - "workflow" + description: "Book a schedule in all DCs of the workflow. Must set a desired\ + \ schedule first!" + operationId: "WorkflowController.Book Schedule" + parameters: + - name: "workflowName" + in: "path" + description: "Workflow Name" + required: true + type: "string" + x-exportParamName: "WorkflowName" + responses: + "200": + description: "" + schema: + type: "array" + items: + $ref: "#/definitions/models.DCstatus" + "401": + description: "Other error. Check output" + "403": + description: "Authentication issue" + /workflow/{workflowName}/schedule/check: + get: + tags: + - "workflow" + description: "Check if we can schedule the project in other DCs. Must set a\ + \ desired schedule first!" + operationId: "WorkflowController.Check Schedule" + parameters: + - name: "workflowName" + in: "path" + description: "Workflow Name" + required: true + type: "string" + x-exportParamName: "WorkflowName" + responses: + "200": + description: "" + schema: + type: "array" + items: + $ref: "#/definitions/models.DCstatus" + "401": + description: "Other error" + "403": + description: "Authentication issue" + /workspace/: + post: + tags: + - "workspace" + description: "Insert a resource in the workspace" + operationId: "WorkspaceController.Add model to workspace" + parameters: + - name: "id" + in: "query" + description: "ID of a resource" + required: true + type: "string" + x-exportParamName: "Id" + - name: "rtype" + in: "query" + description: "Type of resource" + required: true + type: "string" + x-exportParamName: "Rtype" + responses: + "200": + description: "{string} login success" + "400": + description: "{string} Other error" + "403": + description: "Authentication issue" + delete: + tags: + - "workspace" + description: "Remove a resource from the workspace" + operationId: "WorkspaceController.Delete element from user workspace" + parameters: + - name: "id" + in: "query" + description: "ID of a resource" + required: true + type: "string" + x-exportParamName: "Id" + - name: "rtype" + in: "query" + description: "Type of resource" + required: true + type: "string" + x-exportParamName: "Rtype" + responses: + "200": + description: "{string} Removed succeful" + "400": + description: "{string} Other error" + "403": + description: "Authentication issue" + /workspace/list: + get: + tags: + - "workspace" + description: "Get workspace elements based on user_id token" + operationId: "WorkspaceController.Get workspace" + parameters: [] + responses: + "200": + description: "" + schema: + $ref: "#/definitions/models.Workspace" + "403": + description: "Authentication issue" + /workspace/list_model: + get: + tags: + - "workspace" + description: "Get full workspace elements based on user_id token" + operationId: "WorkspaceController.Get full workspace" + parameters: [] + responses: + "200": + description: "" + schema: + $ref: "#/definitions/models.WorkspaceModel" + "403": + description: "Authentication issue" +definitions: + '*time.Time': + type: "object" + title: "Time" + models.ComputingModel: + type: "object" + required: + - "ID" + properties: + ID: + type: "string" + example: "5099803df3f4948bd2f98391" + description: + type: "string" + execution_requirements: + $ref: "#/definitions/models.ExecutionRequirementsModel" + license: + type: "string" + logo: + type: "string" + name: + type: "string" + description: "Name of the computing" + owner: + type: "string" + price: + type: "integer" + format: "int32" + repository: + $ref: "#/definitions/models.RepositoryModel" + short_description: + type: "string" + type: + type: "string" + title: "ComputingModel" + example: + owner: "owner" + license: "license" + short_description: "short_description" + price: 5 + name: "name" + description: "description" + logo: "logo" + execution_requirements: + cpus: 0 + parallel: true + scaling_model: 5 + gpus: 6 + disk_io: "disk_io" + ram: 1 + ID: "5099803df3f4948bd2f98391" + repository: + credentials: "credentials" + url: "url" + type: "type" + models.ComputingNEWModel: + type: "object" + required: + - "description" + - "logo" + - "name" + - "short_description" + - "type" + properties: + description: + type: "string" + execution_requirements: + $ref: "#/definitions/models.ExecutionRequirementsModel" + license: + type: "string" + logo: + type: "string" + name: + type: "string" + description: "Name of the computing" + owner: + type: "string" + price: + type: "integer" + format: "int32" + repository: + $ref: "#/definitions/models.RepositoryModel" + short_description: + type: "string" + type: + type: "string" + title: "ComputingNEWModel" + models.ComputingObject: + type: "object" + properties: + datacenterID: + type: "string" + description: "Datacenter where the computing will be executed" + inputs: + type: "array" + items: + type: "string" + outputs: + type: "array" + items: + type: "string" + referenceID: + description: "Computing model ID" + $ref: "#/definitions/primitive.ObjectID" + title: "ComputingObject" + example: + outputs: + - "outputs" + - "outputs" + inputs: + - "inputs" + - "inputs" + datacenterID: "datacenterID" + referenceID: {} + models.DCstatus: + type: "object" + properties: + Booked: + $ref: "#/definitions/models.ScheduleInfo" + DCname: + type: "string" + DCobjID: + type: "string" + ErrorMessage: + type: "string" + IsAvailable: + type: "boolean" + IsReachable: + type: "boolean" + title: "DCstatus" + example: + DCobjID: "DCobjID" + DCname: "DCname" + IsAvailable: true + Booked: + Total: 0 + NextExecutions: + - "NextExecutions" + - "NextExecutions" + IsReachable: true + ErrorMessage: "ErrorMessage" + models.DataModel: + type: "object" + required: + - "ID" + properties: + ID: + type: "string" + description: + type: "string" + example: + type: "string" + description: "base64 encoded data" + ftype: + type: "string" + location: + type: "string" + logo: + type: "string" + name: + type: "string" + description: "Name of the data" + protocol: + type: "array" + items: + type: "string" + short_description: + type: "string" + type: + type: "string" + example: "file" + description: "Define type of data" + title: "DataModel" + example: + short_description: "short_description" + protocol: + - "protocol" + - "protocol" + ftype: "ftype" + name: "name" + description: "description" + logo: "logo" + location: "location" + ID: "ID" + type: "file" + example: "example" + models.DataNEWModel: + type: "object" + required: + - "description" + - "example" + - "location" + - "logo" + - "name" + - "short_description" + - "type" + properties: + description: + type: "string" + example: + type: "string" + description: "base64 encoded data" + ftype: + type: "string" + location: + type: "string" + logo: + type: "string" + name: + type: "string" + description: "Name of the data" + protocol: + type: "array" + items: + type: "string" + short_description: + type: "string" + type: + type: "string" + example: "file" + description: "Define type of data" + title: "DataNEWModel" + models.DataObject: + type: "object" + properties: + referenceID: + description: "Data model ID" + $ref: "#/definitions/primitive.ObjectID" + title: "DataObject" + example: {} + models.DatacenterCpuModel: + type: "object" + required: + - "cores" + properties: + architecture: + type: "string" + cores: + type: "integer" + format: "int32" + minimum_memory: + type: "integer" + format: "int32" + platform: + type: "string" + shared: + type: "boolean" + title: "DatacenterCpuModel" + example: + shared: true + cores: 6 + platform: "platform" + architecture: "architecture" + minimum_memory: 1 + models.DatacenterGpuModel: + type: "object" + properties: + cuda_cores: + type: "integer" + format: "int32" + memory: + type: "integer" + format: "int32" + description: "Units in MB" + model: + type: "string" + tensor_cores: + type: "integer" + format: "int32" + title: "DatacenterGpuModel" + example: + memory: 5 + cuda_cores: 5 + model: "model" + tensor_cores: 2 + models.DatacenterMemoryModel: + type: "object" + properties: + ecc: + type: "boolean" + size: + type: "integer" + format: "int32" + description: "Units in MB" + title: "DatacenterMemoryModel" + example: + ecc: true + size: 7 + models.DatacenterModel: + type: "object" + required: + - "ID" + properties: + ID: + type: "string" + acronym: + type: "string" + description: "id of the DC" + bookingPrice: + type: "integer" + format: "int64" + cpu: + $ref: "#/definitions/models.DatacenterCpuModel" + description: + type: "string" + gpu: + type: "array" + items: + $ref: "#/definitions/models.DatacenterGpuModel" + hosts: + type: "array" + description: "list of host:port" + items: + type: "string" + logo: + type: "string" + name: + type: "string" + owner: + type: "string" + ram: + $ref: "#/definitions/models.DatacenterMemoryModel" + short_description: + type: "string" + type: + type: "string" + title: "DatacenterModel" + example: + owner: "owner" + short_description: "short_description" + acronym: "acronym" + hosts: + - "hosts" + - "hosts" + cpu: + shared: true + cores: 6 + platform: "platform" + architecture: "architecture" + minimum_memory: 1 + description: "description" + type: "type" + gpu: + - memory: 5 + cuda_cores: 5 + model: "model" + tensor_cores: 2 + - memory: 5 + cuda_cores: 5 + model: "model" + tensor_cores: 2 + bookingPrice: 0 + name: "name" + logo: "logo" + ID: "ID" + ram: + ecc: true + size: 7 + models.DatacenterNEWModel: + type: "object" + required: + - "acronym" + - "cpu" + - "description" + - "gpu" + - "hosts" + - "logo" + - "name" + - "ram" + - "short_description" + - "type" + properties: + acronym: + type: "string" + description: "id of the DC" + bookingPrice: + type: "integer" + format: "int64" + cpu: + $ref: "#/definitions/models.DatacenterCpuModel" + description: + type: "string" + gpu: + type: "array" + items: + $ref: "#/definitions/models.DatacenterGpuModel" + hosts: + type: "array" + description: "list of host:port" + items: + type: "string" + logo: + type: "string" + name: + type: "string" + owner: + type: "string" + ram: + $ref: "#/definitions/models.DatacenterMemoryModel" + short_description: + type: "string" + type: + type: "string" + title: "DatacenterNEWModel" + models.DatacenterObject: + type: "object" + properties: + referenceID: + description: "Data model ID" + $ref: "#/definitions/primitive.ObjectID" + title: "DatacenterObject" + example: {} + models.ExecutionRequirementsModel: + type: "object" + required: + - "cpus" + - "ram" + properties: + cpus: + type: "integer" + format: "int32" + disk_io: + type: "string" + gpus: + type: "integer" + format: "int32" + description: "Amount of GPUs needed" + parallel: + type: "boolean" + ram: + type: "integer" + format: "int32" + description: "Units in MB" + scaling_model: + type: "integer" + format: "int32" + title: "ExecutionRequirementsModel" + example: + cpus: 0 + parallel: true + scaling_model: 5 + gpus: 6 + disk_io: "disk_io" + ram: 1 + models.RepositoryModel: + type: "object" + properties: + credentials: + type: "string" + url: + type: "string" + title: "RepositoryModel" + example: + credentials: "credentials" + url: "url" + models.ScheduleDB: + type: "object" + properties: + ResourceQty: + $ref: "#/definitions/models.ExecutionRequirementsModel" + StartDate: + type: "string" + format: "datetime" + StopDate: + type: "string" + format: "datetime" + Workflow: + type: "string" + title: "ScheduleDB" + example: + StartDate: "StartDate" + ResourceQty: + cpus: 0 + parallel: true + scaling_model: 5 + gpus: 6 + disk_io: "disk_io" + ram: 1 + StopDate: "StopDate" + Workflow: "Workflow" + models.ScheduleInfo: + type: "object" + properties: + NextExecutions: + type: "array" + items: + type: "string" + Total: + type: "integer" + format: "int64" + title: "ScheduleInfo" + example: + Total: 0 + NextExecutions: + - "NextExecutions" + - "NextExecutions" + models.ScheduleTime: + type: "object" + title: "ScheduleTime" + models.SearchResult: + type: "object" + required: + - "computing" + properties: + computing: + type: "array" + items: + $ref: "#/definitions/models.ComputingModel" + data: + type: "array" + items: + $ref: "#/definitions/models.DataModel" + datacenter: + type: "array" + items: + $ref: "#/definitions/models.DatacenterModel" + storage: + type: "array" + items: + $ref: "#/definitions/models.StorageModel" + title: "SearchResult" + example: + computing: + - owner: "owner" + license: "license" + short_description: "short_description" + price: 5 + name: "name" + description: "description" + logo: "logo" + execution_requirements: + cpus: 0 + parallel: true + scaling_model: 5 + gpus: 6 + disk_io: "disk_io" + ram: 1 + ID: "5099803df3f4948bd2f98391" + repository: + credentials: "credentials" + url: "url" + type: "type" + - owner: "owner" + license: "license" + short_description: "short_description" + price: 5 + name: "name" + description: "description" + logo: "logo" + execution_requirements: + cpus: 0 + parallel: true + scaling_model: 5 + gpus: 6 + disk_io: "disk_io" + ram: 1 + ID: "5099803df3f4948bd2f98391" + repository: + credentials: "credentials" + url: "url" + type: "type" + data: + - short_description: "short_description" + protocol: + - "protocol" + - "protocol" + ftype: "ftype" + name: "name" + description: "description" + logo: "logo" + location: "location" + ID: "ID" + type: "file" + example: "example" + - short_description: "short_description" + protocol: + - "protocol" + - "protocol" + ftype: "ftype" + name: "name" + description: "description" + logo: "logo" + location: "location" + ID: "ID" + type: "file" + example: "example" + datacenter: + - owner: "owner" + short_description: "short_description" + acronym: "acronym" + hosts: + - "hosts" + - "hosts" + cpu: + shared: true + cores: 6 + platform: "platform" + architecture: "architecture" + minimum_memory: 1 + description: "description" + type: "type" + gpu: + - memory: 5 + cuda_cores: 5 + model: "model" + tensor_cores: 2 + - memory: 5 + cuda_cores: 5 + model: "model" + tensor_cores: 2 + bookingPrice: 0 + name: "name" + logo: "logo" + ID: "ID" + ram: + ecc: true + size: 7 + - owner: "owner" + short_description: "short_description" + acronym: "acronym" + hosts: + - "hosts" + - "hosts" + cpu: + shared: true + cores: 6 + platform: "platform" + architecture: "architecture" + minimum_memory: 1 + description: "description" + type: "type" + gpu: + - memory: 5 + cuda_cores: 5 + model: "model" + tensor_cores: 2 + - memory: 5 + cuda_cores: 5 + model: "model" + tensor_cores: 2 + bookingPrice: 0 + name: "name" + logo: "logo" + ID: "ID" + ram: + ecc: true + size: 7 + storage: + - short_description: "short_description" + encryption: true + size: 6 + bookingPrice: 0 + DCacronym: "DCacronym" + name: "name" + description: "description" + logo: "logo" + ID: "ID" + redundancy: "redundancy" + throughput: "throughput" + type: "type" + - short_description: "short_description" + encryption: true + size: 6 + bookingPrice: 0 + DCacronym: "DCacronym" + name: "name" + description: "description" + logo: "logo" + ID: "ID" + redundancy: "redundancy" + throughput: "throughput" + type: "type" + models.StorageModel: + type: "object" + required: + - "ID" + properties: + DCacronym: + type: "string" + description: "Unique ID of the DC where it is the storage" + ID: + type: "string" + bookingPrice: + type: "integer" + format: "int32" + description: + type: "string" + encryption: + type: "boolean" + logo: + type: "string" + name: + type: "string" + redundancy: + type: "string" + short_description: + type: "string" + size: + type: "integer" + format: "int32" + throughput: + type: "string" + type: + type: "string" + title: "StorageModel" + example: + short_description: "short_description" + encryption: true + size: 6 + bookingPrice: 0 + DCacronym: "DCacronym" + name: "name" + description: "description" + logo: "logo" + ID: "ID" + redundancy: "redundancy" + throughput: "throughput" + type: "type" + models.StorageNEWModel: + type: "object" + required: + - "DCacronym" + - "description" + - "logo" + - "name" + - "short_description" + - "size" + - "type" + properties: + DCacronym: + type: "string" + description: "Unique ID of the DC where it is the storage" + bookingPrice: + type: "integer" + format: "int32" + description: + type: "string" + encryption: + type: "boolean" + logo: + type: "string" + name: + type: "string" + redundancy: + type: "string" + short_description: + type: "string" + size: + type: "integer" + format: "int32" + throughput: + type: "string" + type: + type: "string" + title: "StorageNEWModel" + models.StorageObject: + type: "object" + properties: + inputs: + type: "array" + items: + type: "string" + outputs: + type: "array" + items: + type: "string" + referenceID: + description: "Storage model ID" + $ref: "#/definitions/primitive.ObjectID" + title: "StorageObject" + example: + outputs: + - "outputs" + - "outputs" + inputs: + - "inputs" + - "inputs" + models.Workflow: + type: "object" + properties: + MxgraphXML: + type: "string" + description: "State of the mxgraph" + computing: + $ref: "#/definitions/models.ComputingObject" + data: + $ref: "#/definitions/models.DataObject" + datacenter: + $ref: "#/definitions/models.DatacenterObject" + schedules: + $ref: "#/definitions/models.WorkflowSchedule" + storage: + $ref: "#/definitions/models.StorageObject" + title: "Workflow" + example: + computing: + outputs: + - "outputs" + - "outputs" + inputs: + - "inputs" + - "inputs" + datacenterID: "datacenterID" + referenceID: {} + data: {} + schedules: + StartDate: "StartDate" + cron: "cron" + duration: 7200 + StopDate: "StopDate" + isBooked: true + IsService: true + events: "events" + datacenter: {} + storage: + outputs: + - "outputs" + - "outputs" + inputs: + - "inputs" + - "inputs" + MxgraphXML: "MxgraphXML" + models.WorkflowSchedule: + type: "object" + properties: + IsService: + type: "boolean" + description: "Service: true, Task: false" + StartDate: + type: "string" + format: "datetime" + StopDate: + type: "string" + format: "datetime" + cron: + type: "string" + duration: + type: "integer" + format: "int32" + example: 7200 + description: "Durantion in seconds" + events: + type: "string" + isBooked: + type: "boolean" + title: "WorkflowSchedule" + example: + StartDate: "StartDate" + cron: "cron" + duration: 7200 + StopDate: "StopDate" + isBooked: true + IsService: true + events: "events" + models.Workspace: + type: "object" + properties: + Workflows: + $ref: "#/definitions/models.Workflow" + computing: + type: "array" + items: + type: "string" + data: + type: "array" + items: + type: "string" + datacenter: + type: "array" + items: + type: "string" + storage: + type: "array" + items: + type: "string" + user_id: + type: "string" + title: "Workspace" + example: + computing: + - "computing" + - "computing" + data: + - "data" + - "data" + Workflows: + computing: + outputs: + - "outputs" + - "outputs" + inputs: + - "inputs" + - "inputs" + datacenterID: "datacenterID" + referenceID: {} + data: {} + schedules: + StartDate: "StartDate" + cron: "cron" + duration: 7200 + StopDate: "StopDate" + isBooked: true + IsService: true + events: "events" + datacenter: {} + storage: + outputs: + - "outputs" + - "outputs" + inputs: + - "inputs" + - "inputs" + MxgraphXML: "MxgraphXML" + user_id: "user_id" + datacenter: + - "datacenter" + - "datacenter" + storage: + - "storage" + - "storage" + models.WorkspaceModel: + type: "object" + properties: + computing: + type: "array" + items: + $ref: "#/definitions/models.ComputingModel" + data: + type: "array" + items: + $ref: "#/definitions/models.DataModel" + datacenter: + type: "array" + items: + $ref: "#/definitions/models.DatacenterModel" + storage: + type: "array" + items: + $ref: "#/definitions/models.StorageModel" + user_id: + type: "string" + title: "WorkspaceModel" + example: + computing: + - owner: "owner" + license: "license" + short_description: "short_description" + price: 5 + name: "name" + description: "description" + logo: "logo" + execution_requirements: + cpus: 0 + parallel: true + scaling_model: 5 + gpus: 6 + disk_io: "disk_io" + ram: 1 + ID: "5099803df3f4948bd2f98391" + repository: + credentials: "credentials" + url: "url" + type: "type" + - owner: "owner" + license: "license" + short_description: "short_description" + price: 5 + name: "name" + description: "description" + logo: "logo" + execution_requirements: + cpus: 0 + parallel: true + scaling_model: 5 + gpus: 6 + disk_io: "disk_io" + ram: 1 + ID: "5099803df3f4948bd2f98391" + repository: + credentials: "credentials" + url: "url" + type: "type" + data: + - short_description: "short_description" + protocol: + - "protocol" + - "protocol" + ftype: "ftype" + name: "name" + description: "description" + logo: "logo" + location: "location" + ID: "ID" + type: "file" + example: "example" + - short_description: "short_description" + protocol: + - "protocol" + - "protocol" + ftype: "ftype" + name: "name" + description: "description" + logo: "logo" + location: "location" + ID: "ID" + type: "file" + example: "example" + user_id: "user_id" + datacenter: + - owner: "owner" + short_description: "short_description" + acronym: "acronym" + hosts: + - "hosts" + - "hosts" + cpu: + shared: true + cores: 6 + platform: "platform" + architecture: "architecture" + minimum_memory: 1 + description: "description" + type: "type" + gpu: + - memory: 5 + cuda_cores: 5 + model: "model" + tensor_cores: 2 + - memory: 5 + cuda_cores: 5 + model: "model" + tensor_cores: 2 + bookingPrice: 0 + name: "name" + logo: "logo" + ID: "ID" + ram: + ecc: true + size: 7 + - owner: "owner" + short_description: "short_description" + acronym: "acronym" + hosts: + - "hosts" + - "hosts" + cpu: + shared: true + cores: 6 + platform: "platform" + architecture: "architecture" + minimum_memory: 1 + description: "description" + type: "type" + gpu: + - memory: 5 + cuda_cores: 5 + model: "model" + tensor_cores: 2 + - memory: 5 + cuda_cores: 5 + model: "model" + tensor_cores: 2 + bookingPrice: 0 + name: "name" + logo: "logo" + ID: "ID" + ram: + ecc: true + size: 7 + storage: + - short_description: "short_description" + encryption: true + size: 6 + bookingPrice: 0 + DCacronym: "DCacronym" + name: "name" + description: "description" + logo: "logo" + ID: "ID" + redundancy: "redundancy" + throughput: "throughput" + type: "type" + - short_description: "short_description" + encryption: true + size: 6 + bookingPrice: 0 + DCacronym: "DCacronym" + name: "name" + description: "description" + logo: "logo" + ID: "ID" + redundancy: "redundancy" + throughput: "throughput" + type: "type" + primitive.ObjectID: + type: "object" + title: "ObjectID" + time.Time: + type: "object" + title: "Time" diff --git a/selfapi/api_computing.go b/selfapi/api_computing.go new file mode 100644 index 0000000..60cfded --- /dev/null +++ b/selfapi/api_computing.go @@ -0,0 +1,269 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +import ( + "context" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +// Linger please +var ( + _ context.Context +) + +type ComputingApiService service + +/* +ComputingApiService +Submit a computing object + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param body The object content +*/ +func (a *ComputingApiService) ComputingControllerAddComputing(ctx context.Context, body ModelsComputingNewModel) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/computing/" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + // body params + localVarPostBody = &body + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +ComputingApiService +Find a computing resource based on ID + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param iD The ID of the resource + +@return ModelsComputingModel +*/ +func (a *ComputingApiService) ComputingControllerGetComputingByID(ctx context.Context, iD string) (ModelsComputingModel, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ModelsComputingModel + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/computing/{ID}" + localVarPath = strings.Replace(localVarPath, "{"+"ID"+"}", fmt.Sprintf("%v", iD), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v ModelsComputingModel + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +ComputingApiService +Return Computing objects if found in the DB. Not found IDs will be ignored + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param iDs List of computing IDs + +@return []ModelsComputingModel +*/ +func (a *ComputingApiService) ComputingControllerGetMultipleComputingByIDs(ctx context.Context, iDs []string) ([]ModelsComputingModel, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue []ModelsComputingModel + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/computing/multi/{IDs}" + localVarPath = strings.Replace(localVarPath, "{"+"IDs"+"}", fmt.Sprintf("%v", iDs), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v []ModelsComputingModel + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} diff --git a/selfapi/api_data.go b/selfapi/api_data.go new file mode 100644 index 0000000..e6d61a5 --- /dev/null +++ b/selfapi/api_data.go @@ -0,0 +1,269 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +import ( + "context" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +// Linger please +var ( + _ context.Context +) + +type DataApiService service + +/* +DataApiService +Submit data object + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param body The object content +*/ +func (a *DataApiService) DataControllerCreateData(ctx context.Context, body ModelsDataNewModel) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/data/" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + // body params + localVarPostBody = &body + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +DataApiService +Find rType data based on ID + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param iD The ID of the data resource + +@return ModelsDataModel +*/ +func (a *DataApiService) DataControllerGetDataByID(ctx context.Context, iD string) (ModelsDataModel, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ModelsDataModel + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/data/{ID}" + localVarPath = strings.Replace(localVarPath, "{"+"ID"+"}", fmt.Sprintf("%v", iD), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v ModelsDataModel + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +DataApiService +Return Data object if found in the DB. Not found IDs will be ignored + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param iDs List of data IDs + +@return []ModelsDataModel +*/ +func (a *DataApiService) DataControllerGetMultipleDataByIDs(ctx context.Context, iDs []string) ([]ModelsDataModel, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue []ModelsDataModel + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/data/multi/{IDs}" + localVarPath = strings.Replace(localVarPath, "{"+"IDs"+"}", fmt.Sprintf("%v", iDs), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v []ModelsDataModel + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} diff --git a/selfapi/api_datacenter.go b/selfapi/api_datacenter.go new file mode 100644 index 0000000..1cdc03c --- /dev/null +++ b/selfapi/api_datacenter.go @@ -0,0 +1,269 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +import ( + "context" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +// Linger please +var ( + _ context.Context +) + +type DatacenterApiService service + +/* +DatacenterApiService +submit Datacenter object + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param body The object content +*/ +func (a *DatacenterApiService) DatacenterControllerCreateDatacenter(ctx context.Context, body ModelsDatacenterNewModel) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/datacenter/" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + // body params + localVarPostBody = &body + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +DatacenterApiService +Return Datacenter objects if found in the DB. Not found IDs will be ignored + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param iDs List of datacenter IDs + +@return []ModelsComputingModel +*/ +func (a *DatacenterApiService) DatacenterControllerGetMultipleDatacentersByIDs(ctx context.Context, iDs []string) ([]ModelsComputingModel, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue []ModelsComputingModel + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/datacenter/multi/{IDs}" + localVarPath = strings.Replace(localVarPath, "{"+"IDs"+"}", fmt.Sprintf("%v", iDs), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v []ModelsComputingModel + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +DatacenterApiService +find datacenter by ID + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param iD the ID you want to get + +@return ModelsDatacenterModel +*/ +func (a *DatacenterApiService) DatacenterControllerGetOneDatacenter(ctx context.Context, iD string) (ModelsDatacenterModel, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ModelsDatacenterModel + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/datacenter/{ID}" + localVarPath = strings.Replace(localVarPath, "{"+"ID"+"}", fmt.Sprintf("%v", iD), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v ModelsDatacenterModel + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} diff --git a/selfapi/api_schedule.go b/selfapi/api_schedule.go new file mode 100644 index 0000000..01bf66f --- /dev/null +++ b/selfapi/api_schedule.go @@ -0,0 +1,465 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +import ( + "context" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +// Linger please +var ( + _ context.Context +) + +type ScheduleApiService service + +/* +ScheduleApiService +Check for availability of this DC + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param cron Cron syntax + - @param duration Duration in seconds + - @param startDate RFC3339 time for startDate + - @param stopDate RFC3339 time for stopDate + - @param requirements The object content +*/ +func (a *ScheduleApiService) ScheduleControllerCheckIfScheduleCanBeCreatedInThisDC(ctx context.Context, cron string, duration int32, startDate interface{}, stopDate interface{}, requirements ModelsExecutionRequirementsModel) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/schedule/check" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("cron", parameterToString(cron, "")) + localVarQueryParams.Add("duration", parameterToString(duration, "")) + localVarQueryParams.Add("startDate", parameterToString(startDate, "")) + localVarQueryParams.Add("stopDate", parameterToString(stopDate, "")) + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + // body params + localVarPostBody = &requirements + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +ScheduleApiService +Create schedule for a workflow. It will return some future executions just as information + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param dcName Name of the node (oc-catalog) from where the workflow comes. + - @param workflowName Workflow Name + - @param cron Cron syntax with year. If no year is specified, will use the current + - @param duration Duration in seconds + - @param startDate RFC3339 time for startDate + - @param stopDate RFC3339 time for stopDate + - @param requirements The object content + +@return ModelsScheduleInfo +*/ +func (a *ScheduleApiService) ScheduleControllerCreateSchedule(ctx context.Context, dcName string, workflowName string, cron string, duration int32, startDate interface{}, stopDate interface{}, requirements ModelsExecutionRequirementsModel) (ModelsScheduleInfo, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ModelsScheduleInfo + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/schedule/book" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("dcName", parameterToString(dcName, "")) + localVarQueryParams.Add("workflowName", parameterToString(workflowName, "")) + localVarQueryParams.Add("cron", parameterToString(cron, "")) + localVarQueryParams.Add("duration", parameterToString(duration, "")) + localVarQueryParams.Add("startDate", parameterToString(startDate, "")) + localVarQueryParams.Add("stopDate", parameterToString(stopDate, "")) + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + // body params + localVarPostBody = &requirements + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v ModelsScheduleInfo + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +ScheduleApiService +Give a date, get the next date where there are at least on schedule. If no hours specified, will assume 00:00 + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param baseDate Base date + +@return TimeTime +*/ +func (a *ScheduleApiService) ScheduleControllerGetNextSchedule(ctx context.Context, baseDate interface{}) (TimeTime, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue TimeTime + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/schedule/next" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("baseDate", parameterToString(baseDate, "")) + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v TimeTime + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +ScheduleApiService +Give a date, get the previous date where there are at least on schedule. If no hours specified, will assume 00:00 + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param baseDate Base date + +@return TimeTime +*/ +func (a *ScheduleApiService) ScheduleControllerGetPreviousSchedule(ctx context.Context, baseDate interface{}) (TimeTime, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue TimeTime + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/schedule/previous" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("baseDate", parameterToString(baseDate, "")) + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v TimeTime + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +ScheduleApiService +Get a list of next startDates schedules (inclusive). If timezone is not specified, will assume UTC + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param startDate Start date + - @param stopDate End date + +@return []ModelsScheduleDb +*/ +func (a *ScheduleApiService) ScheduleControllerGetSchedules(ctx context.Context, startDate interface{}, stopDate interface{}) ([]ModelsScheduleDb, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue []ModelsScheduleDb + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/schedule/" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("startDate", parameterToString(startDate, "")) + localVarQueryParams.Add("stopDate", parameterToString(stopDate, "")) + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v []ModelsScheduleDb + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} diff --git a/selfapi/api_search.go b/selfapi/api_search.go new file mode 100644 index 0000000..23f73f1 --- /dev/null +++ b/selfapi/api_search.go @@ -0,0 +1,113 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +import ( + "context" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +// Linger please +var ( + _ context.Context +) + +type SearchApiService service + +/* +SearchApiService +find resources by word + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param word Word to search across all resources + +@return ModelsSearchResult +*/ +func (a *SearchApiService) SearchControllerSearchByWord(ctx context.Context, word string) (ModelsSearchResult, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ModelsSearchResult + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/search/byWord" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("word", parameterToString(word, "")) + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v ModelsSearchResult + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} diff --git a/selfapi/api_storage.go b/selfapi/api_storage.go new file mode 100644 index 0000000..86b61ff --- /dev/null +++ b/selfapi/api_storage.go @@ -0,0 +1,269 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +import ( + "context" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +// Linger please +var ( + _ context.Context +) + +type StorageApiService service + +/* +StorageApiService +submit storage object + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param body The object content +*/ +func (a *StorageApiService) StorageControllerCreateStorage(ctx context.Context, body ModelsStorageNewModel) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/storage/" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + // body params + localVarPostBody = &body + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +StorageApiService +find storage by ID + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param iD the ID you want to get + +@return ModelsStorageModel +*/ +func (a *StorageApiService) StorageControllerGet(ctx context.Context, iD string) (ModelsStorageModel, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ModelsStorageModel + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/storage/{ID}" + localVarPath = strings.Replace(localVarPath, "{"+"ID"+"}", fmt.Sprintf("%v", iD), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v ModelsStorageModel + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +StorageApiService +Return Storage objects if found in the DB. Not found IDs will be ignored + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param iDs List of storage IDs + +@return []ModelsComputingModel +*/ +func (a *StorageApiService) StorageControllerGetMultipleStoragesByIDs(ctx context.Context, iDs []string) ([]ModelsComputingModel, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue []ModelsComputingModel + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/storage/multi/{IDs}" + localVarPath = strings.Replace(localVarPath, "{"+"IDs"+"}", fmt.Sprintf("%v", iDs), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v []ModelsComputingModel + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} diff --git a/selfapi/api_user.go b/selfapi/api_user.go new file mode 100644 index 0000000..0ee82b2 --- /dev/null +++ b/selfapi/api_user.go @@ -0,0 +1,160 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +import ( + "context" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +// Linger please +var ( + _ context.Context +) + +type UserApiService service + +/* +UserApiService +Logs user into the system + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param username The username for login + - @param password The password for login +*/ +func (a *UserApiService) UserControllerLogin(ctx context.Context, username string, password string) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/user/login" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("username", parameterToString(username, "")) + localVarQueryParams.Add("password", parameterToString(password, "")) + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +UserApiService +Logs out current logged in user session + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). +*/ +func (a *UserApiService) UserControllerLogout(ctx context.Context) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/user/logout" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} diff --git a/selfapi/api_workflow.go b/selfapi/api_workflow.go new file mode 100644 index 0000000..77a5dd8 --- /dev/null +++ b/selfapi/api_workflow.go @@ -0,0 +1,901 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +import ( + "context" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strings" + + "github.com/antihax/optional" +) + +// Linger please +var ( + _ context.Context +) + +type WorkflowApiService service + +/* +WorkflowApiService +Create a Rtype object from already added resources to the workspace + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param workflowName workflow Name + - @param rID rID of already existing item in Workspace +*/ +func (a *WorkflowApiService) WorkflowControllerAddNewObjectToAWorkflow(ctx context.Context, workflowName string, rID string) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workflow/{workflowName}/add" + localVarPath = strings.Replace(localVarPath, "{"+"workflowName"+"}", fmt.Sprintf("%v", workflowName), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("rID", parameterToString(rID, "")) + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +WorkflowApiService +Book a schedule in all DCs of the workflow. Must set a desired schedule first! + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param workflowName Workflow Name + +@return []ModelsDCstatus +*/ +func (a *WorkflowApiService) WorkflowControllerBookSchedule(ctx context.Context, workflowName string) ([]ModelsDCstatus, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue []ModelsDCstatus + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workflow/{workflowName}/schedule/book" + localVarPath = strings.Replace(localVarPath, "{"+"workflowName"+"}", fmt.Sprintf("%v", workflowName), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v []ModelsDCstatus + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +WorkflowApiService +Check if we can schedule the project in other DCs. Must set a desired schedule first! + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param workflowName Workflow Name + +@return []ModelsDCstatus +*/ +func (a *WorkflowApiService) WorkflowControllerCheckSchedule(ctx context.Context, workflowName string) ([]ModelsDCstatus, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue []ModelsDCstatus + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workflow/{workflowName}/schedule/check" + localVarPath = strings.Replace(localVarPath, "{"+"workflowName"+"}", fmt.Sprintf("%v", workflowName), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v []ModelsDCstatus + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +WorkflowApiService +Create a name for the new workflow + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param workflowName Name of the workflow +*/ +func (a *WorkflowApiService) WorkflowControllerCreateANewWorkflow(ctx context.Context, workflowName string) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workflow/" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("workflowName", parameterToString(workflowName, "")) + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +WorkflowApiService +Create a Rtype object from already added resources to the workspace + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param workflowName Workflow Name + - @param rObjIDsource Robject source. Usually Data + - @param isInput If the operation is for input (true) linkage or output (false) + - @param rObjIDtarger Robject where will be written the association +*/ +func (a *WorkflowApiService) WorkflowControllerCreateARealtionshipBetweenTwoRobjects(ctx context.Context, workflowName string, rObjIDsource string, isInput bool, rObjIDtarger string) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workflow/{workflowName}/link" + localVarPath = strings.Replace(localVarPath, "{"+"workflowName"+"}", fmt.Sprintf("%v", workflowName), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("rObjIDsource", parameterToString(rObjIDsource, "")) + localVarQueryParams.Add("isInput", parameterToString(isInput, "")) + localVarQueryParams.Add("rObjIDtarger", parameterToString(rObjIDtarger, "")) + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +WorkflowApiService +Obtain the last mxgraph XML status from the workflow + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param workflowName Workflow Name +*/ +func (a *WorkflowApiService) WorkflowControllerGetMxGraphLastStatus(ctx context.Context, workflowName string) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workflow/{workflowName}/mxGraphParser" + localVarPath = strings.Replace(localVarPath, "{"+"workflowName"+"}", fmt.Sprintf("%v", workflowName), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +WorkflowApiService +Obtain the desired schedule of this workflow + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param workflowName Workflow Name + +@return ModelsScheduleTime +*/ +func (a *WorkflowApiService) WorkflowControllerGetSchedule(ctx context.Context, workflowName string) (ModelsScheduleTime, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ModelsScheduleTime + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workflow/{workflowName}/schedule" + localVarPath = strings.Replace(localVarPath, "{"+"workflowName"+"}", fmt.Sprintf("%v", workflowName), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v ModelsScheduleTime + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +WorkflowApiService +Get a workflow by name + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param workflowName Workflow Name + +@return ModelsWorkflow +*/ +func (a *WorkflowApiService) WorkflowControllerGetWorkflow(ctx context.Context, workflowName string) (ModelsWorkflow, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ModelsWorkflow + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workflow/{workflowName}" + localVarPath = strings.Replace(localVarPath, "{"+"workflowName"+"}", fmt.Sprintf("%v", workflowName), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v ModelsWorkflow + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +WorkflowApiService +List available workflows + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). +*/ +func (a *WorkflowApiService) WorkflowControllerListWorkflows(ctx context.Context) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workflow/" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +WorkflowApiService +If we use this aproach to transofrm mxgraph representation in our representation, we should not use other API calls for modify the project structure or we'll have inconsistencies. + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param workflowName Workflow Name + - @param xmlData Xml representation of the workflow +*/ +func (a *WorkflowApiService) WorkflowControllerParseMxGraph(ctx context.Context, workflowName string, xmlData string) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workflow/{workflowName}/mxGraphParser" + localVarPath = strings.Replace(localVarPath, "{"+"workflowName"+"}", fmt.Sprintf("%v", workflowName), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + // body params + localVarPostBody = &xmlData + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +WorkflowApiService +Set desired schedule by the user. No other effects a part of saving the user input + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param workflowName Workflow Name + * @param isService True: Service, False: Task + * @param startDate RFC3339 time for startDate + * @param stopDate RFC3339 time for stopDate + * @param optional nil or *WorkflowApiWorkflowControllerSetScheduleOpts - Optional Parameters: + * @param "Events" (optional.String) - List of events separated by comma + * @param "CronString" (optional.String) - Cron string + * @param "Duration" (optional.Int32) - Duration in seconds + +@return ModelsScheduleInfo +*/ + +type WorkflowApiWorkflowControllerSetScheduleOpts struct { + Events optional.String + CronString optional.String + Duration optional.Int32 +} + +func (a *WorkflowApiService) WorkflowControllerSetSchedule(ctx context.Context, workflowName string, isService bool, startDate interface{}, stopDate interface{}, localVarOptionals *WorkflowApiWorkflowControllerSetScheduleOpts) (ModelsScheduleInfo, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Put") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ModelsScheduleInfo + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workflow/{workflowName}/schedule" + localVarPath = strings.Replace(localVarPath, "{"+"workflowName"+"}", fmt.Sprintf("%v", workflowName), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("isService", parameterToString(isService, "")) + localVarQueryParams.Add("startDate", parameterToString(startDate, "")) + localVarQueryParams.Add("stopDate", parameterToString(stopDate, "")) + if localVarOptionals != nil && localVarOptionals.Events.IsSet() { + localVarQueryParams.Add("events", parameterToString(localVarOptionals.Events.Value(), "")) + } + if localVarOptionals != nil && localVarOptionals.CronString.IsSet() { + localVarQueryParams.Add("cronString", parameterToString(localVarOptionals.CronString.Value(), "")) + } + if localVarOptionals != nil && localVarOptionals.Duration.IsSet() { + localVarQueryParams.Add("duration", parameterToString(localVarOptionals.Duration.Value(), "")) + } + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v ModelsScheduleInfo + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} diff --git a/selfapi/api_workspace.go b/selfapi/api_workspace.go new file mode 100644 index 0000000..449abc6 --- /dev/null +++ b/selfapi/api_workspace.go @@ -0,0 +1,334 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +import ( + "context" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +// Linger please +var ( + _ context.Context +) + +type WorkspaceApiService service + +/* +WorkspaceApiService +Insert a resource in the workspace + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param id ID of a resource + - @param rtype Type of resource +*/ +func (a *WorkspaceApiService) WorkspaceControllerAddModelToWorkspace(ctx context.Context, id string, rtype string) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workspace/" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("id", parameterToString(id, "")) + localVarQueryParams.Add("rtype", parameterToString(rtype, "")) + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +WorkspaceApiService +Remove a resource from the workspace + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param id ID of a resource + - @param rtype Type of resource +*/ +func (a *WorkspaceApiService) WorkspaceControllerDeleteElementFromUserWorkspace(ctx context.Context, id string, rtype string) (*http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Delete") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workspace/" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + localVarQueryParams.Add("id", parameterToString(id, "")) + localVarQueryParams.Add("rtype", parameterToString(rtype, "")) + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + return localVarHttpResponse, newErr + } + + return localVarHttpResponse, nil +} + +/* +WorkspaceApiService +Get full workspace elements based on user_id token + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + +@return ModelsWorkspaceModel +*/ +func (a *WorkspaceApiService) WorkspaceControllerGetFullWorkspace(ctx context.Context) (ModelsWorkspaceModel, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ModelsWorkspaceModel + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workspace/list_model" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v ModelsWorkspaceModel + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +WorkspaceApiService +Get workspace elements based on user_id token + - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + +@return ModelsWorkspace +*/ +func (a *WorkspaceApiService) WorkspaceControllerGetWorkspace(ctx context.Context) (ModelsWorkspace, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ModelsWorkspace + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/workspace/list" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v ModelsWorkspace + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} diff --git a/selfapi/client.go b/selfapi/client.go new file mode 100644 index 0000000..46a4509 --- /dev/null +++ b/selfapi/client.go @@ -0,0 +1,499 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +import ( + "bytes" + "context" + "encoding/json" + "encoding/xml" + "errors" + "fmt" + "io" + "mime/multipart" + "net/http" + "net/url" + "os" + "path/filepath" + "reflect" + "regexp" + "strconv" + "strings" + "time" + "unicode/utf8" + + "golang.org/x/oauth2" +) + +var ( + jsonCheck = regexp.MustCompile("(?i:(?:application|text)/json)") + xmlCheck = regexp.MustCompile("(?i:(?:application|text)/xml)") +) + +// APIClient manages communication with the oc-catalog API API v1.0.0 +// In most cases there should be only one, shared, APIClient. +type APIClient struct { + cfg *Configuration + common service // Reuse a single struct instead of allocating one for each service on the heap. + + // API Services + + ComputingApi *ComputingApiService + + DataApi *DataApiService + + DatacenterApi *DatacenterApiService + + ScheduleApi *ScheduleApiService + + SearchApi *SearchApiService + + StorageApi *StorageApiService + + UserApi *UserApiService + + WorkflowApi *WorkflowApiService + + WorkspaceApi *WorkspaceApiService +} + +type service struct { + client *APIClient +} + +// NewAPIClient creates a new API client. Requires a userAgent string describing your application. +// optionally a custom http.Client to allow for advanced features such as caching. +func NewAPIClient(cfg *Configuration) *APIClient { + if cfg.HTTPClient == nil { + cfg.HTTPClient = http.DefaultClient + } + + c := &APIClient{} + c.cfg = cfg + c.common.client = c + + // API Services + c.ComputingApi = (*ComputingApiService)(&c.common) + c.DataApi = (*DataApiService)(&c.common) + c.DatacenterApi = (*DatacenterApiService)(&c.common) + c.ScheduleApi = (*ScheduleApiService)(&c.common) + c.SearchApi = (*SearchApiService)(&c.common) + c.StorageApi = (*StorageApiService)(&c.common) + c.UserApi = (*UserApiService)(&c.common) + c.WorkflowApi = (*WorkflowApiService)(&c.common) + c.WorkspaceApi = (*WorkspaceApiService)(&c.common) + + return c +} + +func atoi(in string) (int, error) { + return strconv.Atoi(in) +} + +// selectHeaderContentType select a content type from the available list. +func selectHeaderContentType(contentTypes []string) string { + if len(contentTypes) == 0 { + return "" + } + if contains(contentTypes, "application/json") { + return "application/json" + } + return contentTypes[0] // use the first content type specified in 'consumes' +} + +// selectHeaderAccept join all accept types and return +func selectHeaderAccept(accepts []string) string { + if len(accepts) == 0 { + return "" + } + + if contains(accepts, "application/json") { + return "application/json" + } + + return strings.Join(accepts, ",") +} + +// contains is a case insenstive match, finding needle in a haystack +func contains(haystack []string, needle string) bool { + for _, a := range haystack { + if strings.ToLower(a) == strings.ToLower(needle) { + return true + } + } + return false +} + +// Verify optional parameters are of the correct type. +func typeCheckParameter(obj interface{}, expected string, name string) error { + // Make sure there is an object. + if obj == nil { + return nil + } + + // Check the type is as expected. + if reflect.TypeOf(obj).String() != expected { + return fmt.Errorf("Expected %s to be of type %s but received %s.", name, expected, reflect.TypeOf(obj).String()) + } + return nil +} + +// parameterToString convert interface{} parameters to string, using a delimiter if format is provided. +func parameterToString(obj interface{}, collectionFormat string) string { + var delimiter string + + switch collectionFormat { + case "pipes": + delimiter = "|" + case "ssv": + delimiter = " " + case "tsv": + delimiter = "\t" + case "csv": + delimiter = "," + } + + if reflect.TypeOf(obj).Kind() == reflect.Slice { + return strings.Trim(strings.Replace(fmt.Sprint(obj), " ", delimiter, -1), "[]") + } + + return fmt.Sprintf("%v", obj) +} + +// callAPI do the request. +func (c *APIClient) callAPI(request *http.Request) (*http.Response, error) { + return c.cfg.HTTPClient.Do(request) +} + +// Change base path to allow switching to mocks +func (c *APIClient) ChangeBasePath(path string) { + c.cfg.BasePath = path +} + +// prepareRequest build the request +func (c *APIClient) prepareRequest( + ctx context.Context, + path string, method string, + postBody interface{}, + headerParams map[string]string, + queryParams url.Values, + formParams url.Values, + fileName string, + fileBytes []byte) (localVarRequest *http.Request, err error) { + + var body *bytes.Buffer + + // Detect postBody type and post. + if postBody != nil { + contentType := headerParams["Content-Type"] + if contentType == "" { + contentType = detectContentType(postBody) + headerParams["Content-Type"] = contentType + } + + body, err = setBody(postBody, contentType) + if err != nil { + return nil, err + } + } + + // add form parameters and file if available. + if strings.HasPrefix(headerParams["Content-Type"], "multipart/form-data") && len(formParams) > 0 || (len(fileBytes) > 0 && fileName != "") { + if body != nil { + return nil, errors.New("Cannot specify postBody and multipart form at the same time.") + } + body = &bytes.Buffer{} + w := multipart.NewWriter(body) + + for k, v := range formParams { + for _, iv := range v { + if strings.HasPrefix(k, "@") { // file + err = addFile(w, k[1:], iv) + if err != nil { + return nil, err + } + } else { // form value + w.WriteField(k, iv) + } + } + } + if len(fileBytes) > 0 && fileName != "" { + w.Boundary() + //_, fileNm := filepath.Split(fileName) + part, err := w.CreateFormFile("file", filepath.Base(fileName)) + if err != nil { + return nil, err + } + _, err = part.Write(fileBytes) + if err != nil { + return nil, err + } + // Set the Boundary in the Content-Type + headerParams["Content-Type"] = w.FormDataContentType() + } + + // Set Content-Length + headerParams["Content-Length"] = fmt.Sprintf("%d", body.Len()) + w.Close() + } + + if strings.HasPrefix(headerParams["Content-Type"], "application/x-www-form-urlencoded") && len(formParams) > 0 { + if body != nil { + return nil, errors.New("Cannot specify postBody and x-www-form-urlencoded form at the same time.") + } + body = &bytes.Buffer{} + body.WriteString(formParams.Encode()) + // Set Content-Length + headerParams["Content-Length"] = fmt.Sprintf("%d", body.Len()) + } + + // Setup path and query parameters + url, err := url.Parse(path) + if err != nil { + return nil, err + } + + // Adding Query Param + query := url.Query() + for k, v := range queryParams { + for _, iv := range v { + query.Add(k, iv) + } + } + + // Encode the parameters. + url.RawQuery = query.Encode() + + // Generate a new request + if body != nil { + localVarRequest, err = http.NewRequest(method, url.String(), body) + } else { + localVarRequest, err = http.NewRequest(method, url.String(), nil) + } + if err != nil { + return nil, err + } + + // add header parameters, if any + if len(headerParams) > 0 { + headers := http.Header{} + for h, v := range headerParams { + headers.Set(h, v) + } + localVarRequest.Header = headers + } + + // Override request host, if applicable + if c.cfg.Host != "" { + localVarRequest.Host = c.cfg.Host + } + + // Add the user agent to the request. + localVarRequest.Header.Add("User-Agent", c.cfg.UserAgent) + + if ctx != nil { + // add context to the request + localVarRequest = localVarRequest.WithContext(ctx) + + // Walk through any authentication. + + // OAuth2 authentication + if tok, ok := ctx.Value(ContextOAuth2).(oauth2.TokenSource); ok { + // We were able to grab an oauth2 token from the context + var latestToken *oauth2.Token + if latestToken, err = tok.Token(); err != nil { + return nil, err + } + + latestToken.SetAuthHeader(localVarRequest) + } + + // Basic HTTP Authentication + if auth, ok := ctx.Value(ContextBasicAuth).(BasicAuth); ok { + localVarRequest.SetBasicAuth(auth.UserName, auth.Password) + } + + // AccessToken Authentication + if auth, ok := ctx.Value(ContextAccessToken).(string); ok { + localVarRequest.Header.Add("Authorization", "Bearer "+auth) + } + } + + for header, value := range c.cfg.DefaultHeader { + localVarRequest.Header.Add(header, value) + } + + return localVarRequest, nil +} + +func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err error) { + if strings.Contains(contentType, "application/xml") { + if err = xml.Unmarshal(b, v); err != nil { + return err + } + return nil + } else if strings.Contains(contentType, "application/json") { + if err = json.Unmarshal(b, v); err != nil { + return err + } + return nil + } + return errors.New("undefined response type") +} + +// Add a file to the multipart request +func addFile(w *multipart.Writer, fieldName, path string) error { + file, err := os.Open(path) + if err != nil { + return err + } + defer file.Close() + + part, err := w.CreateFormFile(fieldName, filepath.Base(path)) + if err != nil { + return err + } + _, err = io.Copy(part, file) + + return err +} + +// Prevent trying to import "fmt" +func reportError(format string, a ...interface{}) error { + return fmt.Errorf(format, a...) +} + +// Set request body from an interface{} +func setBody(body interface{}, contentType string) (bodyBuf *bytes.Buffer, err error) { + if bodyBuf == nil { + bodyBuf = &bytes.Buffer{} + } + + if reader, ok := body.(io.Reader); ok { + _, err = bodyBuf.ReadFrom(reader) + } else if b, ok := body.([]byte); ok { + _, err = bodyBuf.Write(b) + } else if s, ok := body.(string); ok { + _, err = bodyBuf.WriteString(s) + } else if s, ok := body.(*string); ok { + _, err = bodyBuf.WriteString(*s) + } else if jsonCheck.MatchString(contentType) { + err = json.NewEncoder(bodyBuf).Encode(body) + } else if xmlCheck.MatchString(contentType) { + xml.NewEncoder(bodyBuf).Encode(body) + } + + if err != nil { + return nil, err + } + + if bodyBuf.Len() == 0 { + err = fmt.Errorf("Invalid body type %s\n", contentType) + return nil, err + } + return bodyBuf, nil +} + +// detectContentType method is used to figure out `Request.Body` content type for request header +func detectContentType(body interface{}) string { + contentType := "text/plain; charset=utf-8" + kind := reflect.TypeOf(body).Kind() + + switch kind { + case reflect.Struct, reflect.Map, reflect.Ptr: + contentType = "application/json; charset=utf-8" + case reflect.String: + contentType = "text/plain; charset=utf-8" + default: + if b, ok := body.([]byte); ok { + contentType = http.DetectContentType(b) + } else if kind == reflect.Slice { + contentType = "application/json; charset=utf-8" + } + } + + return contentType +} + +// Ripped from https://github.com/gregjones/httpcache/blob/master/httpcache.go +type cacheControl map[string]string + +func parseCacheControl(headers http.Header) cacheControl { + cc := cacheControl{} + ccHeader := headers.Get("Cache-Control") + for _, part := range strings.Split(ccHeader, ",") { + part = strings.Trim(part, " ") + if part == "" { + continue + } + if strings.ContainsRune(part, '=') { + keyval := strings.Split(part, "=") + cc[strings.Trim(keyval[0], " ")] = strings.Trim(keyval[1], ",") + } else { + cc[part] = "" + } + } + return cc +} + +// CacheExpires helper function to determine remaining time before repeating a request. +func CacheExpires(r *http.Response) time.Time { + // Figure out when the cache expires. + var expires time.Time + now, err := time.Parse(time.RFC1123, r.Header.Get("date")) + if err != nil { + return time.Now() + } + respCacheControl := parseCacheControl(r.Header) + + if maxAge, ok := respCacheControl["max-age"]; ok { + lifetime, err := time.ParseDuration(maxAge + "s") + if err != nil { + expires = now + } + expires = now.Add(lifetime) + } else { + expiresHeader := r.Header.Get("Expires") + if expiresHeader != "" { + expires, err = time.Parse(time.RFC1123, expiresHeader) + if err != nil { + expires = now + } + } + } + return expires +} + +func strlen(s string) int { + return utf8.RuneCountInString(s) +} + +// GenericSwaggerError Provides access to the body, error and model on returned errors. +type GenericSwaggerError struct { + body []byte + error string + model interface{} +} + +// Error returns non-empty string if there was an error. +func (e GenericSwaggerError) Error() string { + return e.error +} + +// Body returns the raw bytes of the response +func (e GenericSwaggerError) Body() []byte { + return e.body +} + +// Model returns the unpacked model of the error +func (e GenericSwaggerError) Model() interface{} { + return e.model +} diff --git a/selfapi/configuration.go b/selfapi/configuration.go new file mode 100644 index 0000000..418994a --- /dev/null +++ b/selfapi/configuration.go @@ -0,0 +1,73 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +import ( + "net/http" +) + +// contextKeys are used to identify the type of value in the context. +// Since these are string, it is possible to get a short description of the +// context key for logging and debugging using key.String(). + +type contextKey string + +func (c contextKey) String() string { + return "auth " + string(c) +} + +var ( + // ContextOAuth2 takes a oauth2.TokenSource as authentication for the request. + ContextOAuth2 = contextKey("token") + + // ContextBasicAuth takes BasicAuth as authentication for the request. + ContextBasicAuth = contextKey("basic") + + // ContextAccessToken takes a string oauth2 access token as authentication for the request. + ContextAccessToken = contextKey("accesstoken") + + // ContextAPIKey takes an APIKey as authentication for the request + ContextAPIKey = contextKey("apikey") +) + +// BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth +type BasicAuth struct { + UserName string `json:"userName,omitempty"` + Password string `json:"password,omitempty"` +} + +// APIKey provides API key based authentication to a request passed via context using ContextAPIKey +type APIKey struct { + Key string + Prefix string +} + +type Configuration struct { + BasePath string `json:"basePath,omitempty"` + Host string `json:"host,omitempty"` + Scheme string `json:"scheme,omitempty"` + DefaultHeader map[string]string `json:"defaultHeader,omitempty"` + UserAgent string `json:"userAgent,omitempty"` + HTTPClient *http.Client +} + +func NewConfiguration() *Configuration { + cfg := &Configuration{ + BasePath: "https://localhost:49618/v1", + DefaultHeader: make(map[string]string), + UserAgent: "Swagger-Codegen/1.0.0/go", + } + return cfg +} + +func (c *Configuration) AddDefaultHeader(key string, value string) { + c.DefaultHeader[key] = value +} diff --git a/selfapi/docs/ComputingApi.md b/selfapi/docs/ComputingApi.md new file mode 100644 index 0000000..a5f26e2 --- /dev/null +++ b/selfapi/docs/ComputingApi.md @@ -0,0 +1,95 @@ +# \ComputingApi + +All URIs are relative to *https://localhost:49618/v1* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**ComputingControllerAddComputing**](ComputingApi.md#ComputingControllerAddComputing) | **Post** /computing/ | +[**ComputingControllerGetComputingByID**](ComputingApi.md#ComputingControllerGetComputingByID) | **Get** /computing/{ID} | +[**ComputingControllerGetMultipleComputingByIDs**](ComputingApi.md#ComputingControllerGetMultipleComputingByIDs) | **Get** /computing/multi/{IDs} | + + +# **ComputingControllerAddComputing** +> ComputingControllerAddComputing(ctx, body) + + +Submit a computing object + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **body** | [**ModelsComputingNewModel**](ModelsComputingNewModel.md)| The object content | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **ComputingControllerGetComputingByID** +> ModelsComputingModel ComputingControllerGetComputingByID(ctx, iD) + + +Find a computing resource based on ID + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **iD** | **string**| The ID of the resource | + +### Return type + +[**ModelsComputingModel**](models.ComputingModel.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **ComputingControllerGetMultipleComputingByIDs** +> []ModelsComputingModel ComputingControllerGetMultipleComputingByIDs(ctx, iDs) + + +Return Computing objects if found in the DB. Not found IDs will be ignored + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **iDs** | [**[]string**](string.md)| List of computing IDs | + +### Return type + +[**[]ModelsComputingModel**](models.ComputingModel.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/selfapi/docs/DataApi.md b/selfapi/docs/DataApi.md new file mode 100644 index 0000000..fa31aa8 --- /dev/null +++ b/selfapi/docs/DataApi.md @@ -0,0 +1,95 @@ +# \DataApi + +All URIs are relative to *https://localhost:49618/v1* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**DataControllerCreateData**](DataApi.md#DataControllerCreateData) | **Post** /data/ | +[**DataControllerGetDataByID**](DataApi.md#DataControllerGetDataByID) | **Get** /data/{ID} | +[**DataControllerGetMultipleDataByIDs**](DataApi.md#DataControllerGetMultipleDataByIDs) | **Get** /data/multi/{IDs} | + + +# **DataControllerCreateData** +> DataControllerCreateData(ctx, body) + + +Submit data object + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **body** | [**ModelsDataNewModel**](ModelsDataNewModel.md)| The object content | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **DataControllerGetDataByID** +> ModelsDataModel DataControllerGetDataByID(ctx, iD) + + +Find rType data based on ID + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **iD** | **string**| The ID of the data resource | + +### Return type + +[**ModelsDataModel**](models.DataModel.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **DataControllerGetMultipleDataByIDs** +> []ModelsDataModel DataControllerGetMultipleDataByIDs(ctx, iDs) + + +Return Data object if found in the DB. Not found IDs will be ignored + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **iDs** | [**[]string**](string.md)| List of data IDs | + +### Return type + +[**[]ModelsDataModel**](models.DataModel.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/selfapi/docs/DatacenterApi.md b/selfapi/docs/DatacenterApi.md new file mode 100644 index 0000000..dc1b10e --- /dev/null +++ b/selfapi/docs/DatacenterApi.md @@ -0,0 +1,95 @@ +# \DatacenterApi + +All URIs are relative to *https://localhost:49618/v1* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**DatacenterControllerCreateDatacenter**](DatacenterApi.md#DatacenterControllerCreateDatacenter) | **Post** /datacenter/ | +[**DatacenterControllerGetMultipleDatacentersByIDs**](DatacenterApi.md#DatacenterControllerGetMultipleDatacentersByIDs) | **Get** /datacenter/multi/{IDs} | +[**DatacenterControllerGetOneDatacenter**](DatacenterApi.md#DatacenterControllerGetOneDatacenter) | **Get** /datacenter/{ID} | + + +# **DatacenterControllerCreateDatacenter** +> DatacenterControllerCreateDatacenter(ctx, body) + + +submit Datacenter object + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **body** | [**ModelsDatacenterNewModel**](ModelsDatacenterNewModel.md)| The object content | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **DatacenterControllerGetMultipleDatacentersByIDs** +> []ModelsComputingModel DatacenterControllerGetMultipleDatacentersByIDs(ctx, iDs) + + +Return Datacenter objects if found in the DB. Not found IDs will be ignored + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **iDs** | [**[]string**](string.md)| List of datacenter IDs | + +### Return type + +[**[]ModelsComputingModel**](models.ComputingModel.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **DatacenterControllerGetOneDatacenter** +> ModelsDatacenterModel DatacenterControllerGetOneDatacenter(ctx, iD) + + +find datacenter by ID + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **iD** | **string**| the ID you want to get | + +### Return type + +[**ModelsDatacenterModel**](models.DatacenterModel.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/selfapi/docs/ModelsComputingModel.md b/selfapi/docs/ModelsComputingModel.md new file mode 100644 index 0000000..030935d --- /dev/null +++ b/selfapi/docs/ModelsComputingModel.md @@ -0,0 +1,20 @@ +# ModelsComputingModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**ID** | **string** | | [default to null] +**Description** | **string** | | [optional] [default to null] +**ExecutionRequirements** | [***ModelsExecutionRequirementsModel**](models.ExecutionRequirementsModel.md) | | [optional] [default to null] +**License** | **string** | | [optional] [default to null] +**Logo** | **string** | | [optional] [default to null] +**Name** | **string** | Name of the computing | [optional] [default to null] +**Owner** | **string** | | [optional] [default to null] +**Price** | **int32** | | [optional] [default to null] +**Repository** | [***ModelsRepositoryModel**](models.RepositoryModel.md) | | [optional] [default to null] +**ShortDescription** | **string** | | [optional] [default to null] +**Type_** | **string** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsComputingNewModel.md b/selfapi/docs/ModelsComputingNewModel.md new file mode 100644 index 0000000..7a0f539 --- /dev/null +++ b/selfapi/docs/ModelsComputingNewModel.md @@ -0,0 +1,19 @@ +# ModelsComputingNewModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Description** | **string** | | [default to null] +**ExecutionRequirements** | [***ModelsExecutionRequirementsModel**](models.ExecutionRequirementsModel.md) | | [optional] [default to null] +**License** | **string** | | [optional] [default to null] +**Logo** | **string** | | [default to null] +**Name** | **string** | Name of the computing | [default to null] +**Owner** | **string** | | [optional] [default to null] +**Price** | **int32** | | [optional] [default to null] +**Repository** | [***ModelsRepositoryModel**](models.RepositoryModel.md) | | [optional] [default to null] +**ShortDescription** | **string** | | [default to null] +**Type_** | **string** | | [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsComputingObject.md b/selfapi/docs/ModelsComputingObject.md new file mode 100644 index 0000000..6b90469 --- /dev/null +++ b/selfapi/docs/ModelsComputingObject.md @@ -0,0 +1,13 @@ +# ModelsComputingObject + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**DatacenterID** | **string** | Datacenter where the computing will be executed | [optional] [default to null] +**Inputs** | **[]string** | | [optional] [default to null] +**Outputs** | **[]string** | | [optional] [default to null] +**ReferenceID** | [***PrimitiveObjectId**](primitive.ObjectID.md) | Computing model ID | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsDCstatus.md b/selfapi/docs/ModelsDCstatus.md new file mode 100644 index 0000000..fffee88 --- /dev/null +++ b/selfapi/docs/ModelsDCstatus.md @@ -0,0 +1,15 @@ +# ModelsDCstatus + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Booked** | [***ModelsScheduleInfo**](models.ScheduleInfo.md) | | [optional] [default to null] +**DCname** | **string** | | [optional] [default to null] +**DCobjID** | **string** | | [optional] [default to null] +**ErrorMessage** | **string** | | [optional] [default to null] +**IsAvailable** | **bool** | | [optional] [default to null] +**IsReachable** | **bool** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsDataModel.md b/selfapi/docs/ModelsDataModel.md new file mode 100644 index 0000000..388179d --- /dev/null +++ b/selfapi/docs/ModelsDataModel.md @@ -0,0 +1,19 @@ +# ModelsDataModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**ID** | **string** | | [default to null] +**Description** | **string** | | [optional] [default to null] +**Example** | **string** | base64 encoded data | [optional] [default to null] +**Ftype** | **string** | | [optional] [default to null] +**Location** | **string** | | [optional] [default to null] +**Logo** | **string** | | [optional] [default to null] +**Name** | **string** | Name of the data | [optional] [default to null] +**Protocol** | **[]string** | | [optional] [default to null] +**ShortDescription** | **string** | | [optional] [default to null] +**Type_** | **string** | Define type of data | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsDataNewModel.md b/selfapi/docs/ModelsDataNewModel.md new file mode 100644 index 0000000..082835d --- /dev/null +++ b/selfapi/docs/ModelsDataNewModel.md @@ -0,0 +1,18 @@ +# ModelsDataNewModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Description** | **string** | | [default to null] +**Example** | **string** | base64 encoded data | [default to null] +**Ftype** | **string** | | [optional] [default to null] +**Location** | **string** | | [default to null] +**Logo** | **string** | | [default to null] +**Name** | **string** | Name of the data | [default to null] +**Protocol** | **[]string** | | [optional] [default to null] +**ShortDescription** | **string** | | [default to null] +**Type_** | **string** | Define type of data | [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsDataObject.md b/selfapi/docs/ModelsDataObject.md new file mode 100644 index 0000000..895e7c9 --- /dev/null +++ b/selfapi/docs/ModelsDataObject.md @@ -0,0 +1,10 @@ +# ModelsDataObject + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**ReferenceID** | [***PrimitiveObjectId**](primitive.ObjectID.md) | Data model ID | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsDatacenterCpuModel.md b/selfapi/docs/ModelsDatacenterCpuModel.md new file mode 100644 index 0000000..94b967c --- /dev/null +++ b/selfapi/docs/ModelsDatacenterCpuModel.md @@ -0,0 +1,14 @@ +# ModelsDatacenterCpuModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Architecture** | **string** | | [optional] [default to null] +**Cores** | **int32** | | [default to null] +**MinimumMemory** | **int32** | | [optional] [default to null] +**Platform** | **string** | | [optional] [default to null] +**Shared** | **bool** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsDatacenterGpuModel.md b/selfapi/docs/ModelsDatacenterGpuModel.md new file mode 100644 index 0000000..e21255e --- /dev/null +++ b/selfapi/docs/ModelsDatacenterGpuModel.md @@ -0,0 +1,13 @@ +# ModelsDatacenterGpuModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**CudaCores** | **int32** | | [optional] [default to null] +**Memory** | **int32** | Units in MB | [optional] [default to null] +**Model** | **string** | | [optional] [default to null] +**TensorCores** | **int32** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsDatacenterMemoryModel.md b/selfapi/docs/ModelsDatacenterMemoryModel.md new file mode 100644 index 0000000..8332114 --- /dev/null +++ b/selfapi/docs/ModelsDatacenterMemoryModel.md @@ -0,0 +1,11 @@ +# ModelsDatacenterMemoryModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Ecc** | **bool** | | [optional] [default to null] +**Size** | **int32** | Units in MB | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsDatacenterModel.md b/selfapi/docs/ModelsDatacenterModel.md new file mode 100644 index 0000000..0e7e65b --- /dev/null +++ b/selfapi/docs/ModelsDatacenterModel.md @@ -0,0 +1,22 @@ +# ModelsDatacenterModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**ID** | **string** | | [default to null] +**Acronym** | **string** | id of the DC | [optional] [default to null] +**BookingPrice** | **int64** | | [optional] [default to null] +**Cpu** | [***ModelsDatacenterCpuModel**](models.DatacenterCpuModel.md) | | [optional] [default to null] +**Description** | **string** | | [optional] [default to null] +**Gpu** | [**[]ModelsDatacenterGpuModel**](models.DatacenterGpuModel.md) | | [optional] [default to null] +**Hosts** | **[]string** | list of host:port | [optional] [default to null] +**Logo** | **string** | | [optional] [default to null] +**Name** | **string** | | [optional] [default to null] +**Owner** | **string** | | [optional] [default to null] +**Ram** | [***ModelsDatacenterMemoryModel**](models.DatacenterMemoryModel.md) | | [optional] [default to null] +**ShortDescription** | **string** | | [optional] [default to null] +**Type_** | **string** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsDatacenterNewModel.md b/selfapi/docs/ModelsDatacenterNewModel.md new file mode 100644 index 0000000..1ff92f9 --- /dev/null +++ b/selfapi/docs/ModelsDatacenterNewModel.md @@ -0,0 +1,21 @@ +# ModelsDatacenterNewModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Acronym** | **string** | id of the DC | [default to null] +**BookingPrice** | **int64** | | [optional] [default to null] +**Cpu** | [***ModelsDatacenterCpuModel**](models.DatacenterCpuModel.md) | | [default to null] +**Description** | **string** | | [default to null] +**Gpu** | [**[]ModelsDatacenterGpuModel**](models.DatacenterGpuModel.md) | | [default to null] +**Hosts** | **[]string** | list of host:port | [default to null] +**Logo** | **string** | | [default to null] +**Name** | **string** | | [default to null] +**Owner** | **string** | | [optional] [default to null] +**Ram** | [***ModelsDatacenterMemoryModel**](models.DatacenterMemoryModel.md) | | [default to null] +**ShortDescription** | **string** | | [default to null] +**Type_** | **string** | | [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsDatacenterObject.md b/selfapi/docs/ModelsDatacenterObject.md new file mode 100644 index 0000000..ca25aaa --- /dev/null +++ b/selfapi/docs/ModelsDatacenterObject.md @@ -0,0 +1,10 @@ +# ModelsDatacenterObject + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**ReferenceID** | [***PrimitiveObjectId**](primitive.ObjectID.md) | Data model ID | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsExecutionRequirementsModel.md b/selfapi/docs/ModelsExecutionRequirementsModel.md new file mode 100644 index 0000000..c2e2851 --- /dev/null +++ b/selfapi/docs/ModelsExecutionRequirementsModel.md @@ -0,0 +1,15 @@ +# ModelsExecutionRequirementsModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Cpus** | **int32** | | [default to null] +**DiskIo** | **string** | | [optional] [default to null] +**Gpus** | **int32** | Amount of GPUs needed | [optional] [default to null] +**Parallel** | **bool** | | [optional] [default to null] +**Ram** | **int32** | Units in MB | [default to null] +**ScalingModel** | **int32** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsRepositoryModel.md b/selfapi/docs/ModelsRepositoryModel.md new file mode 100644 index 0000000..046f8f1 --- /dev/null +++ b/selfapi/docs/ModelsRepositoryModel.md @@ -0,0 +1,11 @@ +# ModelsRepositoryModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Credentials** | **string** | | [optional] [default to null] +**Url** | **string** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsScheduleDb.md b/selfapi/docs/ModelsScheduleDb.md new file mode 100644 index 0000000..1a3cd78 --- /dev/null +++ b/selfapi/docs/ModelsScheduleDb.md @@ -0,0 +1,13 @@ +# ModelsScheduleDb + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**ResourceQty** | [***ModelsExecutionRequirementsModel**](models.ExecutionRequirementsModel.md) | | [optional] [default to null] +**StartDate** | **string** | | [optional] [default to null] +**StopDate** | **string** | | [optional] [default to null] +**Workflow** | **string** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsScheduleInfo.md b/selfapi/docs/ModelsScheduleInfo.md new file mode 100644 index 0000000..7df98b1 --- /dev/null +++ b/selfapi/docs/ModelsScheduleInfo.md @@ -0,0 +1,11 @@ +# ModelsScheduleInfo + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**NextExecutions** | **[]string** | | [optional] [default to null] +**Total** | **int64** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsScheduleTime.md b/selfapi/docs/ModelsScheduleTime.md new file mode 100644 index 0000000..f6d5731 --- /dev/null +++ b/selfapi/docs/ModelsScheduleTime.md @@ -0,0 +1,9 @@ +# ModelsScheduleTime + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsSearchResult.md b/selfapi/docs/ModelsSearchResult.md new file mode 100644 index 0000000..e064143 --- /dev/null +++ b/selfapi/docs/ModelsSearchResult.md @@ -0,0 +1,13 @@ +# ModelsSearchResult + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Computing** | [**[]ModelsComputingModel**](models.ComputingModel.md) | | [default to null] +**Data** | [**[]ModelsDataModel**](models.DataModel.md) | | [optional] [default to null] +**Datacenter** | [**[]ModelsDatacenterModel**](models.DatacenterModel.md) | | [optional] [default to null] +**Storage** | [**[]ModelsStorageModel**](models.StorageModel.md) | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsStorageModel.md b/selfapi/docs/ModelsStorageModel.md new file mode 100644 index 0000000..67da13e --- /dev/null +++ b/selfapi/docs/ModelsStorageModel.md @@ -0,0 +1,21 @@ +# ModelsStorageModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**DCacronym** | **string** | Unique ID of the DC where it is the storage | [optional] [default to null] +**ID** | **string** | | [default to null] +**BookingPrice** | **int32** | | [optional] [default to null] +**Description** | **string** | | [optional] [default to null] +**Encryption** | **bool** | | [optional] [default to null] +**Logo** | **string** | | [optional] [default to null] +**Name** | **string** | | [optional] [default to null] +**Redundancy** | **string** | | [optional] [default to null] +**ShortDescription** | **string** | | [optional] [default to null] +**Size** | **int32** | | [optional] [default to null] +**Throughput** | **string** | | [optional] [default to null] +**Type_** | **string** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsStorageNewModel.md b/selfapi/docs/ModelsStorageNewModel.md new file mode 100644 index 0000000..ee1d552 --- /dev/null +++ b/selfapi/docs/ModelsStorageNewModel.md @@ -0,0 +1,20 @@ +# ModelsStorageNewModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**DCacronym** | **string** | Unique ID of the DC where it is the storage | [default to null] +**BookingPrice** | **int32** | | [optional] [default to null] +**Description** | **string** | | [default to null] +**Encryption** | **bool** | | [optional] [default to null] +**Logo** | **string** | | [default to null] +**Name** | **string** | | [default to null] +**Redundancy** | **string** | | [optional] [default to null] +**ShortDescription** | **string** | | [default to null] +**Size** | **int32** | | [default to null] +**Throughput** | **string** | | [optional] [default to null] +**Type_** | **string** | | [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsStorageObject.md b/selfapi/docs/ModelsStorageObject.md new file mode 100644 index 0000000..244eab5 --- /dev/null +++ b/selfapi/docs/ModelsStorageObject.md @@ -0,0 +1,12 @@ +# ModelsStorageObject + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Inputs** | **[]string** | | [optional] [default to null] +**Outputs** | **[]string** | | [optional] [default to null] +**ReferenceID** | [***PrimitiveObjectId**](primitive.ObjectID.md) | Storage model ID | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsWorkflow.md b/selfapi/docs/ModelsWorkflow.md new file mode 100644 index 0000000..f023ba2 --- /dev/null +++ b/selfapi/docs/ModelsWorkflow.md @@ -0,0 +1,15 @@ +# ModelsWorkflow + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**MxgraphXML** | **string** | State of the mxgraph | [optional] [default to null] +**Computing** | [***ModelsComputingObject**](models.ComputingObject.md) | | [optional] [default to null] +**Data** | [***ModelsDataObject**](models.DataObject.md) | | [optional] [default to null] +**Datacenter** | [***ModelsDatacenterObject**](models.DatacenterObject.md) | | [optional] [default to null] +**Schedules** | [***ModelsWorkflowSchedule**](models.WorkflowSchedule.md) | | [optional] [default to null] +**Storage** | [***ModelsStorageObject**](models.StorageObject.md) | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsWorkflowSchedule.md b/selfapi/docs/ModelsWorkflowSchedule.md new file mode 100644 index 0000000..516c913 --- /dev/null +++ b/selfapi/docs/ModelsWorkflowSchedule.md @@ -0,0 +1,16 @@ +# ModelsWorkflowSchedule + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**IsService** | **bool** | Service: true, Task: false | [optional] [default to null] +**StartDate** | **string** | | [optional] [default to null] +**StopDate** | **string** | | [optional] [default to null] +**Cron** | **string** | | [optional] [default to null] +**Duration** | **int32** | Durantion in seconds | [optional] [default to null] +**Events** | **string** | | [optional] [default to null] +**IsBooked** | **bool** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsWorkspace.md b/selfapi/docs/ModelsWorkspace.md new file mode 100644 index 0000000..d7f36b9 --- /dev/null +++ b/selfapi/docs/ModelsWorkspace.md @@ -0,0 +1,15 @@ +# ModelsWorkspace + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Workflows** | [***ModelsWorkflow**](models.Workflow.md) | | [optional] [default to null] +**Computing** | **[]string** | | [optional] [default to null] +**Data** | **[]string** | | [optional] [default to null] +**Datacenter** | **[]string** | | [optional] [default to null] +**Storage** | **[]string** | | [optional] [default to null] +**UserId** | **string** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ModelsWorkspaceModel.md b/selfapi/docs/ModelsWorkspaceModel.md new file mode 100644 index 0000000..bf646a1 --- /dev/null +++ b/selfapi/docs/ModelsWorkspaceModel.md @@ -0,0 +1,14 @@ +# ModelsWorkspaceModel + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Computing** | [**[]ModelsComputingModel**](models.ComputingModel.md) | | [optional] [default to null] +**Data** | [**[]ModelsDataModel**](models.DataModel.md) | | [optional] [default to null] +**Datacenter** | [**[]ModelsDatacenterModel**](models.DatacenterModel.md) | | [optional] [default to null] +**Storage** | [**[]ModelsStorageModel**](models.StorageModel.md) | | [optional] [default to null] +**UserId** | **string** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/PrimitiveObjectId.md b/selfapi/docs/PrimitiveObjectId.md new file mode 100644 index 0000000..011ef59 --- /dev/null +++ b/selfapi/docs/PrimitiveObjectId.md @@ -0,0 +1,9 @@ +# PrimitiveObjectId + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/ScheduleApi.md b/selfapi/docs/ScheduleApi.md new file mode 100644 index 0000000..73a6e70 --- /dev/null +++ b/selfapi/docs/ScheduleApi.md @@ -0,0 +1,164 @@ +# \ScheduleApi + +All URIs are relative to *https://localhost:49618/v1* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**ScheduleControllerCheckIfScheduleCanBeCreatedInThisDC**](ScheduleApi.md#ScheduleControllerCheckIfScheduleCanBeCreatedInThisDC) | **Post** /schedule/check | +[**ScheduleControllerCreateSchedule**](ScheduleApi.md#ScheduleControllerCreateSchedule) | **Post** /schedule/book | +[**ScheduleControllerGetNextSchedule**](ScheduleApi.md#ScheduleControllerGetNextSchedule) | **Get** /schedule/next | +[**ScheduleControllerGetPreviousSchedule**](ScheduleApi.md#ScheduleControllerGetPreviousSchedule) | **Get** /schedule/previous | +[**ScheduleControllerGetSchedules**](ScheduleApi.md#ScheduleControllerGetSchedules) | **Get** /schedule/ | + + +# **ScheduleControllerCheckIfScheduleCanBeCreatedInThisDC** +> ScheduleControllerCheckIfScheduleCanBeCreatedInThisDC(ctx, cron, duration, startDate, stopDate, requirements) + + +Check for availability of this DC + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **cron** | **string**| Cron syntax | + **duration** | **int32**| Duration in seconds | + **startDate** | [**interface{}**](.md)| RFC3339 time for startDate | + **stopDate** | [**interface{}**](.md)| RFC3339 time for stopDate | + **requirements** | [**ModelsExecutionRequirementsModel**](ModelsExecutionRequirementsModel.md)| The object content | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **ScheduleControllerCreateSchedule** +> ModelsScheduleInfo ScheduleControllerCreateSchedule(ctx, dcName, workflowName, cron, duration, startDate, stopDate, requirements) + + +Create schedule for a workflow. It will return some future executions just as information + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **dcName** | **string**| Name of the node (oc-catalog) from where the workflow comes. | + **workflowName** | **string**| Workflow Name | + **cron** | **string**| Cron syntax with year. If no year is specified, will use the current | + **duration** | **int32**| Duration in seconds | + **startDate** | [**interface{}**](.md)| RFC3339 time for startDate | + **stopDate** | [**interface{}**](.md)| RFC3339 time for stopDate | + **requirements** | [**ModelsExecutionRequirementsModel**](ModelsExecutionRequirementsModel.md)| The object content | + +### Return type + +[**ModelsScheduleInfo**](models.ScheduleInfo.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **ScheduleControllerGetNextSchedule** +> TimeTime ScheduleControllerGetNextSchedule(ctx, baseDate) + + +Give a date, get the next date where there are at least on schedule. If no hours specified, will assume 00:00 + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **baseDate** | [**interface{}**](.md)| Base date | + +### Return type + +[**TimeTime**](*time.Time.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **ScheduleControllerGetPreviousSchedule** +> TimeTime ScheduleControllerGetPreviousSchedule(ctx, baseDate) + + +Give a date, get the previous date where there are at least on schedule. If no hours specified, will assume 00:00 + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **baseDate** | [**interface{}**](.md)| Base date | + +### Return type + +[**TimeTime**](*time.Time.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **ScheduleControllerGetSchedules** +> []ModelsScheduleDb ScheduleControllerGetSchedules(ctx, startDate, stopDate) + + +Get a list of next startDates schedules (inclusive). If timezone is not specified, will assume UTC + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **startDate** | [**interface{}**](.md)| Start date | + **stopDate** | [**interface{}**](.md)| End date | + +### Return type + +[**[]ModelsScheduleDb**](models.ScheduleDB.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/selfapi/docs/SearchApi.md b/selfapi/docs/SearchApi.md new file mode 100644 index 0000000..b21faf9 --- /dev/null +++ b/selfapi/docs/SearchApi.md @@ -0,0 +1,37 @@ +# \SearchApi + +All URIs are relative to *https://localhost:49618/v1* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**SearchControllerSearchByWord**](SearchApi.md#SearchControllerSearchByWord) | **Get** /search/byWord | + + +# **SearchControllerSearchByWord** +> ModelsSearchResult SearchControllerSearchByWord(ctx, word) + + +find resources by word + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **word** | **string**| Word to search across all resources | + +### Return type + +[**ModelsSearchResult**](models.SearchResult.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/selfapi/docs/StorageApi.md b/selfapi/docs/StorageApi.md new file mode 100644 index 0000000..c024202 --- /dev/null +++ b/selfapi/docs/StorageApi.md @@ -0,0 +1,95 @@ +# \StorageApi + +All URIs are relative to *https://localhost:49618/v1* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**StorageControllerCreateStorage**](StorageApi.md#StorageControllerCreateStorage) | **Post** /storage/ | +[**StorageControllerGet**](StorageApi.md#StorageControllerGet) | **Get** /storage/{ID} | +[**StorageControllerGetMultipleStoragesByIDs**](StorageApi.md#StorageControllerGetMultipleStoragesByIDs) | **Get** /storage/multi/{IDs} | + + +# **StorageControllerCreateStorage** +> StorageControllerCreateStorage(ctx, body) + + +submit storage object + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **body** | [**ModelsStorageNewModel**](ModelsStorageNewModel.md)| The object content | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **StorageControllerGet** +> ModelsStorageModel StorageControllerGet(ctx, iD) + + +find storage by ID + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **iD** | **string**| the ID you want to get | + +### Return type + +[**ModelsStorageModel**](models.StorageModel.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **StorageControllerGetMultipleStoragesByIDs** +> []ModelsComputingModel StorageControllerGetMultipleStoragesByIDs(ctx, iDs) + + +Return Storage objects if found in the DB. Not found IDs will be ignored + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **iDs** | [**[]string**](string.md)| List of storage IDs | + +### Return type + +[**[]ModelsComputingModel**](models.ComputingModel.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/selfapi/docs/TimeTime.md b/selfapi/docs/TimeTime.md new file mode 100644 index 0000000..f1b1d48 --- /dev/null +++ b/selfapi/docs/TimeTime.md @@ -0,0 +1,9 @@ +# TimeTime + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/selfapi/docs/UserApi.md b/selfapi/docs/UserApi.md new file mode 100644 index 0000000..cb8e3f7 --- /dev/null +++ b/selfapi/docs/UserApi.md @@ -0,0 +1,63 @@ +# \UserApi + +All URIs are relative to *https://localhost:49618/v1* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**UserControllerLogin**](UserApi.md#UserControllerLogin) | **Get** /user/login | +[**UserControllerLogout**](UserApi.md#UserControllerLogout) | **Get** /user/logout | + + +# **UserControllerLogin** +> UserControllerLogin(ctx, username, password) + + +Logs user into the system + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **username** | **string**| The username for login | + **password** | **string**| The password for login | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **UserControllerLogout** +> UserControllerLogout(ctx, ) + + +Logs out current logged in user session + +### Required Parameters +This endpoint does not need any parameter. + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/selfapi/docs/WorkflowApi.md b/selfapi/docs/WorkflowApi.md new file mode 100644 index 0000000..2c29535 --- /dev/null +++ b/selfapi/docs/WorkflowApi.md @@ -0,0 +1,345 @@ +# \WorkflowApi + +All URIs are relative to *https://localhost:49618/v1* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**WorkflowControllerAddNewObjectToAWorkflow**](WorkflowApi.md#WorkflowControllerAddNewObjectToAWorkflow) | **Post** /workflow/{workflowName}/add | +[**WorkflowControllerBookSchedule**](WorkflowApi.md#WorkflowControllerBookSchedule) | **Post** /workflow/{workflowName}/schedule/book | +[**WorkflowControllerCheckSchedule**](WorkflowApi.md#WorkflowControllerCheckSchedule) | **Get** /workflow/{workflowName}/schedule/check | +[**WorkflowControllerCreateANewWorkflow**](WorkflowApi.md#WorkflowControllerCreateANewWorkflow) | **Post** /workflow/ | +[**WorkflowControllerCreateARealtionshipBetweenTwoRobjects**](WorkflowApi.md#WorkflowControllerCreateARealtionshipBetweenTwoRobjects) | **Post** /workflow/{workflowName}/link | +[**WorkflowControllerGetMxGraphLastStatus**](WorkflowApi.md#WorkflowControllerGetMxGraphLastStatus) | **Get** /workflow/{workflowName}/mxGraphParser | +[**WorkflowControllerGetSchedule**](WorkflowApi.md#WorkflowControllerGetSchedule) | **Get** /workflow/{workflowName}/schedule | +[**WorkflowControllerGetWorkflow**](WorkflowApi.md#WorkflowControllerGetWorkflow) | **Get** /workflow/{workflowName} | +[**WorkflowControllerListWorkflows**](WorkflowApi.md#WorkflowControllerListWorkflows) | **Get** /workflow/ | +[**WorkflowControllerParseMxGraph**](WorkflowApi.md#WorkflowControllerParseMxGraph) | **Post** /workflow/{workflowName}/mxGraphParser | +[**WorkflowControllerSetSchedule**](WorkflowApi.md#WorkflowControllerSetSchedule) | **Put** /workflow/{workflowName}/schedule | + + +# **WorkflowControllerAddNewObjectToAWorkflow** +> WorkflowControllerAddNewObjectToAWorkflow(ctx, workflowName, rID) + + +Create a Rtype object from already added resources to the workspace + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **workflowName** | **string**| workflow Name | + **rID** | **string**| rID of already existing item in Workspace | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkflowControllerBookSchedule** +> []ModelsDCstatus WorkflowControllerBookSchedule(ctx, workflowName) + + +Book a schedule in all DCs of the workflow. Must set a desired schedule first! + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **workflowName** | **string**| Workflow Name | + +### Return type + +[**[]ModelsDCstatus**](models.DCstatus.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkflowControllerCheckSchedule** +> []ModelsDCstatus WorkflowControllerCheckSchedule(ctx, workflowName) + + +Check if we can schedule the project in other DCs. Must set a desired schedule first! + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **workflowName** | **string**| Workflow Name | + +### Return type + +[**[]ModelsDCstatus**](models.DCstatus.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkflowControllerCreateANewWorkflow** +> WorkflowControllerCreateANewWorkflow(ctx, workflowName) + + +Create a name for the new workflow + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **workflowName** | **string**| Name of the workflow | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkflowControllerCreateARealtionshipBetweenTwoRobjects** +> WorkflowControllerCreateARealtionshipBetweenTwoRobjects(ctx, workflowName, rObjIDsource, isInput, rObjIDtarger) + + +Create a Rtype object from already added resources to the workspace + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **workflowName** | **string**| Workflow Name | + **rObjIDsource** | **string**| Robject source. Usually Data | + **isInput** | **bool**| If the operation is for input (true) linkage or output (false) | + **rObjIDtarger** | **string**| Robject where will be written the association | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkflowControllerGetMxGraphLastStatus** +> WorkflowControllerGetMxGraphLastStatus(ctx, workflowName) + + +Obtain the last mxgraph XML status from the workflow + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **workflowName** | **string**| Workflow Name | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkflowControllerGetSchedule** +> ModelsScheduleTime WorkflowControllerGetSchedule(ctx, workflowName) + + +Obtain the desired schedule of this workflow + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **workflowName** | **string**| Workflow Name | + +### Return type + +[**ModelsScheduleTime**](models.ScheduleTime.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkflowControllerGetWorkflow** +> ModelsWorkflow WorkflowControllerGetWorkflow(ctx, workflowName) + + +Get a workflow by name + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **workflowName** | **string**| Workflow Name | + +### Return type + +[**ModelsWorkflow**](models.Workflow.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkflowControllerListWorkflows** +> WorkflowControllerListWorkflows(ctx, ) + + +List available workflows + +### Required Parameters +This endpoint does not need any parameter. + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkflowControllerParseMxGraph** +> WorkflowControllerParseMxGraph(ctx, workflowName, xmlData) + + +If we use this aproach to transofrm mxgraph representation in our representation, we should not use other API calls for modify the project structure or we'll have inconsistencies. + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **workflowName** | **string**| Workflow Name | + **xmlData** | **string**| Xml representation of the workflow | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkflowControllerSetSchedule** +> ModelsScheduleInfo WorkflowControllerSetSchedule(ctx, workflowName, isService, startDate, stopDate, optional) + + +Set desired schedule by the user. No other effects a part of saving the user input + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **workflowName** | **string**| Workflow Name | + **isService** | **bool**| True: Service, False: Task | + **startDate** | [**interface{}**](.md)| RFC3339 time for startDate | + **stopDate** | [**interface{}**](.md)| RFC3339 time for stopDate | + **optional** | ***WorkflowApiWorkflowControllerSetScheduleOpts** | optional parameters | nil if no parameters + +### Optional Parameters +Optional parameters are passed through a pointer to a WorkflowApiWorkflowControllerSetScheduleOpts struct + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + + + + + **events** | **optional.String**| List of events separated by comma | + **cronString** | **optional.String**| Cron string | + **duration** | **optional.Int32**| Duration in seconds | + +### Return type + +[**ModelsScheduleInfo**](models.ScheduleInfo.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/selfapi/docs/WorkspaceApi.md b/selfapi/docs/WorkspaceApi.md new file mode 100644 index 0000000..1fec5d8 --- /dev/null +++ b/selfapi/docs/WorkspaceApi.md @@ -0,0 +1,118 @@ +# \WorkspaceApi + +All URIs are relative to *https://localhost:49618/v1* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**WorkspaceControllerAddModelToWorkspace**](WorkspaceApi.md#WorkspaceControllerAddModelToWorkspace) | **Post** /workspace/ | +[**WorkspaceControllerDeleteElementFromUserWorkspace**](WorkspaceApi.md#WorkspaceControllerDeleteElementFromUserWorkspace) | **Delete** /workspace/ | +[**WorkspaceControllerGetFullWorkspace**](WorkspaceApi.md#WorkspaceControllerGetFullWorkspace) | **Get** /workspace/list_model | +[**WorkspaceControllerGetWorkspace**](WorkspaceApi.md#WorkspaceControllerGetWorkspace) | **Get** /workspace/list | + + +# **WorkspaceControllerAddModelToWorkspace** +> WorkspaceControllerAddModelToWorkspace(ctx, id, rtype) + + +Insert a resource in the workspace + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **id** | **string**| ID of a resource | + **rtype** | **string**| Type of resource | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkspaceControllerDeleteElementFromUserWorkspace** +> WorkspaceControllerDeleteElementFromUserWorkspace(ctx, id, rtype) + + +Remove a resource from the workspace + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **id** | **string**| ID of a resource | + **rtype** | **string**| Type of resource | + +### Return type + + (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkspaceControllerGetFullWorkspace** +> ModelsWorkspaceModel WorkspaceControllerGetFullWorkspace(ctx, ) + + +Get full workspace elements based on user_id token + +### Required Parameters +This endpoint does not need any parameter. + +### Return type + +[**ModelsWorkspaceModel**](models.WorkspaceModel.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **WorkspaceControllerGetWorkspace** +> ModelsWorkspace WorkspaceControllerGetWorkspace(ctx, ) + + +Get workspace elements based on user_id token + +### Required Parameters +This endpoint does not need any parameter. + +### Return type + +[**ModelsWorkspace**](models.Workspace.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/selfapi/git_push.sh b/selfapi/git_push.sh new file mode 100644 index 0000000..ae01b18 --- /dev/null +++ b/selfapi/git_push.sh @@ -0,0 +1,52 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 swagger-petstore-perl "minor update" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 + +if [ "$git_user_id" = "" ]; then + git_user_id="GIT_USER_ID" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="GIT_REPO_ID" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="Minor update" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=`git remote` +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://github.com/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' + diff --git a/selfapi/model_models_computing_model.go b/selfapi/model_models_computing_model.go new file mode 100644 index 0000000..2c75140 --- /dev/null +++ b/selfapi/model_models_computing_model.go @@ -0,0 +1,26 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsComputingModel struct { + ID string `json:"ID"` + Description string `json:"description,omitempty"` + ExecutionRequirements *ModelsExecutionRequirementsModel `json:"execution_requirements,omitempty"` + License string `json:"license,omitempty"` + Logo string `json:"logo,omitempty"` + // Name of the computing + Name string `json:"name,omitempty"` + Owner string `json:"owner,omitempty"` + Price int32 `json:"price,omitempty"` + Repository *ModelsRepositoryModel `json:"repository,omitempty"` + ShortDescription string `json:"short_description,omitempty"` + Type_ string `json:"type,omitempty"` +} diff --git a/selfapi/model_models_computing_new_model.go b/selfapi/model_models_computing_new_model.go new file mode 100644 index 0000000..8841cbd --- /dev/null +++ b/selfapi/model_models_computing_new_model.go @@ -0,0 +1,25 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsComputingNewModel struct { + Description string `json:"description"` + ExecutionRequirements *ModelsExecutionRequirementsModel `json:"execution_requirements,omitempty"` + License string `json:"license,omitempty"` + Logo string `json:"logo"` + // Name of the computing + Name string `json:"name"` + Owner string `json:"owner,omitempty"` + Price int32 `json:"price,omitempty"` + Repository *ModelsRepositoryModel `json:"repository,omitempty"` + ShortDescription string `json:"short_description"` + Type_ string `json:"type"` +} diff --git a/selfapi/model_models_computing_object.go b/selfapi/model_models_computing_object.go new file mode 100644 index 0000000..61e8fca --- /dev/null +++ b/selfapi/model_models_computing_object.go @@ -0,0 +1,20 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsComputingObject struct { + // Datacenter where the computing will be executed + DatacenterID string `json:"datacenterID,omitempty"` + Inputs []string `json:"inputs,omitempty"` + Outputs []string `json:"outputs,omitempty"` + // Computing model ID + ReferenceID *PrimitiveObjectId `json:"referenceID,omitempty"` +} diff --git a/selfapi/model_models_d_cstatus.go b/selfapi/model_models_d_cstatus.go new file mode 100644 index 0000000..58fd69e --- /dev/null +++ b/selfapi/model_models_d_cstatus.go @@ -0,0 +1,20 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsDCstatus struct { + Booked *ModelsScheduleInfo `json:"Booked,omitempty"` + DCname string `json:"DCname,omitempty"` + DCobjID string `json:"DCobjID,omitempty"` + ErrorMessage string `json:"ErrorMessage,omitempty"` + IsAvailable bool `json:"IsAvailable,omitempty"` + IsReachable bool `json:"IsReachable,omitempty"` +} diff --git a/selfapi/model_models_data_model.go b/selfapi/model_models_data_model.go new file mode 100644 index 0000000..1019309 --- /dev/null +++ b/selfapi/model_models_data_model.go @@ -0,0 +1,27 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsDataModel struct { + ID string `json:"ID"` + Description string `json:"description,omitempty"` + // base64 encoded data + Example string `json:"example,omitempty"` + Ftype string `json:"ftype,omitempty"` + Location string `json:"location,omitempty"` + Logo string `json:"logo,omitempty"` + // Name of the data + Name string `json:"name,omitempty"` + Protocol []string `json:"protocol,omitempty"` + ShortDescription string `json:"short_description,omitempty"` + // Define type of data + Type_ string `json:"type,omitempty"` +} diff --git a/selfapi/model_models_data_new_model.go b/selfapi/model_models_data_new_model.go new file mode 100644 index 0000000..ffc520c --- /dev/null +++ b/selfapi/model_models_data_new_model.go @@ -0,0 +1,26 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsDataNewModel struct { + Description string `json:"description"` + // base64 encoded data + Example string `json:"example"` + Ftype string `json:"ftype,omitempty"` + Location string `json:"location"` + Logo string `json:"logo"` + // Name of the data + Name string `json:"name"` + Protocol []string `json:"protocol,omitempty"` + ShortDescription string `json:"short_description"` + // Define type of data + Type_ string `json:"type"` +} diff --git a/selfapi/model_models_data_object.go b/selfapi/model_models_data_object.go new file mode 100644 index 0000000..376dcb0 --- /dev/null +++ b/selfapi/model_models_data_object.go @@ -0,0 +1,16 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsDataObject struct { + // Data model ID + ReferenceID *PrimitiveObjectId `json:"referenceID,omitempty"` +} diff --git a/selfapi/model_models_datacenter_cpu_model.go b/selfapi/model_models_datacenter_cpu_model.go new file mode 100644 index 0000000..c7599ae --- /dev/null +++ b/selfapi/model_models_datacenter_cpu_model.go @@ -0,0 +1,19 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsDatacenterCpuModel struct { + Architecture string `json:"architecture,omitempty"` + Cores int32 `json:"cores"` + MinimumMemory int32 `json:"minimum_memory,omitempty"` + Platform string `json:"platform,omitempty"` + Shared bool `json:"shared,omitempty"` +} diff --git a/selfapi/model_models_datacenter_gpu_model.go b/selfapi/model_models_datacenter_gpu_model.go new file mode 100644 index 0000000..630451d --- /dev/null +++ b/selfapi/model_models_datacenter_gpu_model.go @@ -0,0 +1,19 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsDatacenterGpuModel struct { + CudaCores int32 `json:"cuda_cores,omitempty"` + // Units in MB + Memory int32 `json:"memory,omitempty"` + Model string `json:"model,omitempty"` + TensorCores int32 `json:"tensor_cores,omitempty"` +} diff --git a/selfapi/model_models_datacenter_memory_model.go b/selfapi/model_models_datacenter_memory_model.go new file mode 100644 index 0000000..3ee913b --- /dev/null +++ b/selfapi/model_models_datacenter_memory_model.go @@ -0,0 +1,17 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsDatacenterMemoryModel struct { + Ecc bool `json:"ecc,omitempty"` + // Units in MB + Size int32 `json:"size,omitempty"` +} diff --git a/selfapi/model_models_datacenter_model.go b/selfapi/model_models_datacenter_model.go new file mode 100644 index 0000000..a761ff8 --- /dev/null +++ b/selfapi/model_models_datacenter_model.go @@ -0,0 +1,29 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsDatacenterModel struct { + ID string `json:"ID"` + // id of the DC + Acronym string `json:"acronym,omitempty"` + BookingPrice int64 `json:"bookingPrice,omitempty"` + Cpu *ModelsDatacenterCpuModel `json:"cpu,omitempty"` + Description string `json:"description,omitempty"` + Gpu []ModelsDatacenterGpuModel `json:"gpu,omitempty"` + // list of host:port + Hosts []string `json:"hosts,omitempty"` + Logo string `json:"logo,omitempty"` + Name string `json:"name,omitempty"` + Owner string `json:"owner,omitempty"` + Ram *ModelsDatacenterMemoryModel `json:"ram,omitempty"` + ShortDescription string `json:"short_description,omitempty"` + Type_ string `json:"type,omitempty"` +} diff --git a/selfapi/model_models_datacenter_new_model.go b/selfapi/model_models_datacenter_new_model.go new file mode 100644 index 0000000..8b3f786 --- /dev/null +++ b/selfapi/model_models_datacenter_new_model.go @@ -0,0 +1,28 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsDatacenterNewModel struct { + // id of the DC + Acronym string `json:"acronym"` + BookingPrice int64 `json:"bookingPrice,omitempty"` + Cpu *ModelsDatacenterCpuModel `json:"cpu"` + Description string `json:"description"` + Gpu []ModelsDatacenterGpuModel `json:"gpu"` + // list of host:port + Hosts []string `json:"hosts"` + Logo string `json:"logo"` + Name string `json:"name"` + Owner string `json:"owner,omitempty"` + Ram *ModelsDatacenterMemoryModel `json:"ram"` + ShortDescription string `json:"short_description"` + Type_ string `json:"type"` +} diff --git a/selfapi/model_models_datacenter_object.go b/selfapi/model_models_datacenter_object.go new file mode 100644 index 0000000..e54a11c --- /dev/null +++ b/selfapi/model_models_datacenter_object.go @@ -0,0 +1,16 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsDatacenterObject struct { + // Data model ID + ReferenceID *PrimitiveObjectId `json:"referenceID,omitempty"` +} diff --git a/selfapi/model_models_execution_requirements_model.go b/selfapi/model_models_execution_requirements_model.go new file mode 100644 index 0000000..8272f80 --- /dev/null +++ b/selfapi/model_models_execution_requirements_model.go @@ -0,0 +1,22 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsExecutionRequirementsModel struct { + Cpus int32 `json:"cpus"` + DiskIo string `json:"disk_io,omitempty"` + // Amount of GPUs needed + Gpus int32 `json:"gpus,omitempty"` + Parallel bool `json:"parallel,omitempty"` + // Units in MB + Ram int32 `json:"ram"` + ScalingModel int32 `json:"scaling_model,omitempty"` +} diff --git a/selfapi/model_models_repository_model.go b/selfapi/model_models_repository_model.go new file mode 100644 index 0000000..d541b06 --- /dev/null +++ b/selfapi/model_models_repository_model.go @@ -0,0 +1,16 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsRepositoryModel struct { + Credentials string `json:"credentials,omitempty"` + Url string `json:"url,omitempty"` +} diff --git a/selfapi/model_models_schedule_db.go b/selfapi/model_models_schedule_db.go new file mode 100644 index 0000000..c5d252c --- /dev/null +++ b/selfapi/model_models_schedule_db.go @@ -0,0 +1,18 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsScheduleDb struct { + ResourceQty *ModelsExecutionRequirementsModel `json:"ResourceQty,omitempty"` + StartDate string `json:"StartDate,omitempty"` + StopDate string `json:"StopDate,omitempty"` + Workflow string `json:"Workflow,omitempty"` +} diff --git a/selfapi/model_models_schedule_info.go b/selfapi/model_models_schedule_info.go new file mode 100644 index 0000000..da3c5bb --- /dev/null +++ b/selfapi/model_models_schedule_info.go @@ -0,0 +1,16 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsScheduleInfo struct { + NextExecutions []string `json:"NextExecutions,omitempty"` + Total int64 `json:"Total,omitempty"` +} diff --git a/selfapi/model_models_schedule_time.go b/selfapi/model_models_schedule_time.go new file mode 100644 index 0000000..49c3bf3 --- /dev/null +++ b/selfapi/model_models_schedule_time.go @@ -0,0 +1,14 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsScheduleTime struct { +} diff --git a/selfapi/model_models_search_result.go b/selfapi/model_models_search_result.go new file mode 100644 index 0000000..91281f4 --- /dev/null +++ b/selfapi/model_models_search_result.go @@ -0,0 +1,18 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsSearchResult struct { + Computing []ModelsComputingModel `json:"computing"` + Data []ModelsDataModel `json:"data,omitempty"` + Datacenter []ModelsDatacenterModel `json:"datacenter,omitempty"` + Storage []ModelsStorageModel `json:"storage,omitempty"` +} diff --git a/selfapi/model_models_storage_model.go b/selfapi/model_models_storage_model.go new file mode 100644 index 0000000..4b85302 --- /dev/null +++ b/selfapi/model_models_storage_model.go @@ -0,0 +1,27 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsStorageModel struct { + // Unique ID of the DC where it is the storage + DCacronym string `json:"DCacronym,omitempty"` + ID string `json:"ID"` + BookingPrice int32 `json:"bookingPrice,omitempty"` + Description string `json:"description,omitempty"` + Encryption bool `json:"encryption,omitempty"` + Logo string `json:"logo,omitempty"` + Name string `json:"name,omitempty"` + Redundancy string `json:"redundancy,omitempty"` + ShortDescription string `json:"short_description,omitempty"` + Size int32 `json:"size,omitempty"` + Throughput string `json:"throughput,omitempty"` + Type_ string `json:"type,omitempty"` +} diff --git a/selfapi/model_models_storage_new_model.go b/selfapi/model_models_storage_new_model.go new file mode 100644 index 0000000..0b97f1f --- /dev/null +++ b/selfapi/model_models_storage_new_model.go @@ -0,0 +1,26 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsStorageNewModel struct { + // Unique ID of the DC where it is the storage + DCacronym string `json:"DCacronym"` + BookingPrice int32 `json:"bookingPrice,omitempty"` + Description string `json:"description"` + Encryption bool `json:"encryption,omitempty"` + Logo string `json:"logo"` + Name string `json:"name"` + Redundancy string `json:"redundancy,omitempty"` + ShortDescription string `json:"short_description"` + Size int32 `json:"size"` + Throughput string `json:"throughput,omitempty"` + Type_ string `json:"type"` +} diff --git a/selfapi/model_models_storage_object.go b/selfapi/model_models_storage_object.go new file mode 100644 index 0000000..114ab49 --- /dev/null +++ b/selfapi/model_models_storage_object.go @@ -0,0 +1,18 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsStorageObject struct { + Inputs []string `json:"inputs,omitempty"` + Outputs []string `json:"outputs,omitempty"` + // Storage model ID + ReferenceID *PrimitiveObjectId `json:"referenceID,omitempty"` +} diff --git a/selfapi/model_models_workflow.go b/selfapi/model_models_workflow.go new file mode 100644 index 0000000..6db7e7a --- /dev/null +++ b/selfapi/model_models_workflow.go @@ -0,0 +1,21 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsWorkflow struct { + // State of the mxgraph + MxgraphXML string `json:"MxgraphXML,omitempty"` + Computing *ModelsComputingObject `json:"computing,omitempty"` + Data *ModelsDataObject `json:"data,omitempty"` + Datacenter *ModelsDatacenterObject `json:"datacenter,omitempty"` + Schedules *ModelsWorkflowSchedule `json:"schedules,omitempty"` + Storage *ModelsStorageObject `json:"storage,omitempty"` +} diff --git a/selfapi/model_models_workflow_schedule.go b/selfapi/model_models_workflow_schedule.go new file mode 100644 index 0000000..6ff9e2c --- /dev/null +++ b/selfapi/model_models_workflow_schedule.go @@ -0,0 +1,23 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsWorkflowSchedule struct { + // Service: true, Task: false + IsService bool `json:"IsService,omitempty"` + StartDate string `json:"StartDate,omitempty"` + StopDate string `json:"StopDate,omitempty"` + Cron string `json:"cron,omitempty"` + // Durantion in seconds + Duration int32 `json:"duration,omitempty"` + Events string `json:"events,omitempty"` + IsBooked bool `json:"isBooked,omitempty"` +} diff --git a/selfapi/model_models_workspace.go b/selfapi/model_models_workspace.go new file mode 100644 index 0000000..4352d82 --- /dev/null +++ b/selfapi/model_models_workspace.go @@ -0,0 +1,20 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsWorkspace struct { + Workflows *ModelsWorkflow `json:"Workflows,omitempty"` + Computing []string `json:"computing,omitempty"` + Data []string `json:"data,omitempty"` + Datacenter []string `json:"datacenter,omitempty"` + Storage []string `json:"storage,omitempty"` + UserId string `json:"user_id,omitempty"` +} diff --git a/selfapi/model_models_workspace_model.go b/selfapi/model_models_workspace_model.go new file mode 100644 index 0000000..89a3388 --- /dev/null +++ b/selfapi/model_models_workspace_model.go @@ -0,0 +1,19 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type ModelsWorkspaceModel struct { + Computing []ModelsComputingModel `json:"computing,omitempty"` + Data []ModelsDataModel `json:"data,omitempty"` + Datacenter []ModelsDatacenterModel `json:"datacenter,omitempty"` + Storage []ModelsStorageModel `json:"storage,omitempty"` + UserId string `json:"user_id,omitempty"` +} diff --git a/selfapi/model_primitive_object_id.go b/selfapi/model_primitive_object_id.go new file mode 100644 index 0000000..15403bf --- /dev/null +++ b/selfapi/model_primitive_object_id.go @@ -0,0 +1,14 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type PrimitiveObjectId struct { +} diff --git a/selfapi/model_time_time.go b/selfapi/model_time_time.go new file mode 100644 index 0000000..76d52bb --- /dev/null +++ b/selfapi/model_time_time.go @@ -0,0 +1,14 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +type TimeTime struct { +} diff --git a/selfapi/response.go b/selfapi/response.go new file mode 100644 index 0000000..5f5f83d --- /dev/null +++ b/selfapi/response.go @@ -0,0 +1,44 @@ +/* + * oc-catalog API + * + * Backend of the oc-search project + * + * API version: 1.0.0 + * Contact: opencloud@irt-saintexupery.com + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package swagger + +import ( + "net/http" +) + +type APIResponse struct { + *http.Response `json:"-"` + Message string `json:"message,omitempty"` + // Operation is the name of the swagger operation. + Operation string `json:"operation,omitempty"` + // RequestURL is the request URL. This value is always available, even if the + // embedded *http.Response is nil. + RequestURL string `json:"url,omitempty"` + // Method is the HTTP method used for the request. This value is always + // available, even if the embedded *http.Response is nil. + Method string `json:"method,omitempty"` + // Payload holds the contents of the response body (which may be nil or empty). + // This is provided here as the raw response.Body() reader will have already + // been drained. + Payload []byte `json:"-"` +} + +func NewAPIResponse(r *http.Response) *APIResponse { + + response := &APIResponse{Response: r} + return response +} + +func NewAPIResponseWithError(errorMessage string) *APIResponse { + + response := &APIResponse{Message: errorMessage} + return response +} diff --git a/services/discovery.go b/services/discovery.go new file mode 100644 index 0000000..f533184 --- /dev/null +++ b/services/discovery.go @@ -0,0 +1,35 @@ +package services + +import ( + "os" + + SelfAPI "cloud.o-forge.io/core/oc-catalog/selfapi" + "github.com/beego/beego/v2/core/logs" + beego "github.com/beego/beego/v2/server/web" +) + +var DC_NAME string + +func GetSelfAPI(host string) *SelfAPI.APIClient { + return SelfAPI.NewAPIClient(&SelfAPI.Configuration{BasePath: "http://" + host + "/v1"}) +} + +func Discoveryinit() { + + dcNameOS := os.Getenv("DOCKER_DCNAME") + if len(dcNameOS) != 0 { + DC_NAME = dcNameOS + return + } + + //FIXME: Beego doesn't retrieve the dcname + beegoDC, err := beego.AppConfig.String("DCNAME") + if err == nil && len(beegoDC) != 0 { + DC_NAME = beegoDC + return + } + + DC_NAME = "DC_DEFAULT" + logs.Warning("Default DC name is used") + +} diff --git a/services/init.go b/services/init.go new file mode 100644 index 0000000..f82ffbf --- /dev/null +++ b/services/init.go @@ -0,0 +1,28 @@ +package services + +import ( + "os" + + "github.com/beego/beego/v2/core/logs" + beego "github.com/beego/beego/v2/server/web" +) + +func Init() { + Discoveryinit() //First init DC name + + var DBpoint string + var err error + + DBpoint = os.Getenv("DOCKER_DBPOINT") + if len(DBpoint) == 0 { + DBpoint, err = beego.AppConfig.String("DBPOINT") + if err != nil { + logs.Critical("DBPOINT URI error: %v", err) + panic(err) + } + + } + + Mongoinit(DC_NAME + "-" + DBpoint) + +} diff --git a/services/mongo.go b/services/mongo.go new file mode 100644 index 0000000..d16f92e --- /dev/null +++ b/services/mongo.go @@ -0,0 +1,157 @@ +package services + +import ( + "context" + "os" + "time" + + "github.com/beego/beego/v2/core/logs" + beego "github.com/beego/beego/v2/server/web" + + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "go.mongodb.org/mongo-driver/x/bsonx" +) + +type MongoCollectionNames struct { + DATA string + COMPUTING string + STORAGE string + DATACENTER string + WORKSPACE string + SCHEDULE string +} + +var ( + mngoClient *mongo.Client + mngoDB *mongo.Database + MngoCtx context.Context + + MngoNamesCollection = MongoCollectionNames{ + DATA: "datas", + COMPUTING: "computings", + STORAGE: "storages", + DATACENTER: "datacenters", + WORKSPACE: "WORKSPACE", + SCHEDULE: "SCHEDULE", + } + + MngoCollData *mongo.Collection + MngoCollComputing *mongo.Collection + MngoCollStorage *mongo.Collection + MngoCollDatacenter *mongo.Collection + MngoCollWorkspace *mongo.Collection + MngoCollSchedule *mongo.Collection +) + +// func GetMongoDBclient() *mongo.Client { +// return mongoDBclient +// } + +func MongoDisconnect() { + if err := mngoClient.Disconnect(MngoCtx); err != nil { + panic(err) + } +} + +func Mongoinit(DBname string) { + + var baseConfig string + + if len(os.Getenv("DOCKER_ENVIRONMENT")) == 0 { + baseConfig = "mongodb" + } else { + baseConfig = "mongodb_docker" + } + + mongoURI, err := beego.AppConfig.String(baseConfig + "::url") + if err != nil { + logs.Critical("MongoDB URI error: %v", err) + panic(err) + } + + logs.Info("Connecting to %v", mongoURI) + + clientOptions := options.Client().ApplyURI(mongoURI) + // mngoClient, err = mongo.NewClient(options.Client().ApplyURI(mongoURI)) + // if err = mngoClient.Connect(MngoCtx); err != nil { + // logs.Critical("Mongodb NewClient %v: %v", mongoURI, err) + // panic(err) + // } + MngoCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + + defer cancel() + + // Ping the primary + if mngoClient, err = mongo.Connect(MngoCtx, clientOptions); err != nil { + logs.Critical("Mongodb Connect %v: %v", mongoURI, err) + panic(err) + } + + if err = mngoClient.Ping(MngoCtx, nil); err != nil { + logs.Critical("Mongodb Ping %v: %v", mongoURI, err) + panic(err) + } + + mngoDB = mngoClient.Database(DBname) + + MngoCollData = mngoDB.Collection(MngoNamesCollection.DATA) + MngoCollComputing = mngoDB.Collection(MngoNamesCollection.COMPUTING) + MngoCollStorage = mngoDB.Collection(MngoNamesCollection.STORAGE) + MngoCollDatacenter = mngoDB.Collection(MngoNamesCollection.DATACENTER) + MngoCollWorkspace = mngoDB.Collection(MngoNamesCollection.WORKSPACE) + MngoCollSchedule = mngoDB.Collection(MngoNamesCollection.SCHEDULE) + + if _, err = MngoCollData.Indexes().CreateMany(MngoCtx, []mongo.IndexModel{ + { + Keys: bsonx.Doc{ + {Key: "description", Value: bsonx.String("text")}, + {Key: "example", Value: bsonx.String("text")}, + }, + }, + }); err != nil && err.(mongo.CommandError).Code != 85 { + logs.Critical(err) + panic(err) + } + + if _, err = MngoCollComputing.Indexes().CreateMany(MngoCtx, []mongo.IndexModel{ + { + Keys: bsonx.Doc{ + {Key: "description", Value: bsonx.String("text")}, + {Key: "owner", Value: bsonx.String("text")}, + {Key: "license", Value: bsonx.String("text")}, + }, + }, + }); err != nil && err.(mongo.CommandError).Code != 85 { + logs.Critical(err) + panic(err) + } + + if _, err = MngoCollStorage.Indexes().CreateMany(MngoCtx, []mongo.IndexModel{ + { + Keys: bsonx.Doc{ + {Key: "name", Value: bsonx.String("text")}, + {Key: "description", Value: bsonx.String("text")}, + }, + }, + }); err != nil && err.(mongo.CommandError).Code != 85 { + logs.Critical(err) + panic(err) + } + + if _, err = MngoCollDatacenter.Indexes().CreateMany(MngoCtx, []mongo.IndexModel{ + { + Keys: bsonx.Doc{ + {Key: "name", Value: bsonx.String("text")}, + {Key: "description", Value: bsonx.String("text")}, + {Key: "owner", Value: bsonx.String("text")}, + }, + }, + }); err != nil && err.(mongo.CommandError).Code != 85 { + logs.Critical(err) + panic(err) + } + + logs.Info("Database is READY") + +}