diff --git a/Makefile b/Makefile index bb3b68449d..a896907e6a 100644 --- a/Makefile +++ b/Makefile @@ -146,7 +146,6 @@ ci-build-images: @./build/build-image.sh images/onnx-serve-gpu onnx-serve-gpu @./build/build-image.sh images/operator operator @./build/build-image.sh images/fluentd fluentd - @./build/build-image.sh images/python-packager python-packager @./build/build-image.sh images/cluster-autoscaler cluster-autoscaler @./build/build-image.sh images/nvidia nvidia @./build/build-image.sh images/metrics-server metrics-server @@ -165,7 +164,6 @@ ci-push-images: @./build/push-image.sh onnx-serve-gpu @./build/push-image.sh operator @./build/push-image.sh fluentd - @./build/push-image.sh python-packager @./build/push-image.sh cluster-autoscaler @./build/push-image.sh nvidia @./build/push-image.sh metrics-server diff --git a/cli/cmd/deploy.go b/cli/cmd/deploy.go index 6a98d601ce..700cf018ec 100644 --- a/cli/cmd/deploy.go +++ b/cli/cmd/deploy.go @@ -18,15 +18,18 @@ package cmd import ( "fmt" + "io/ioutil" "github.com/spf13/cobra" "github.com/cortexlabs/cortex/pkg/lib/console" "github.com/cortexlabs/cortex/pkg/lib/errors" + "github.com/cortexlabs/cortex/pkg/lib/files" "github.com/cortexlabs/cortex/pkg/lib/json" s "github.com/cortexlabs/cortex/pkg/lib/strings" "github.com/cortexlabs/cortex/pkg/lib/zip" "github.com/cortexlabs/cortex/pkg/operator/api/schema" + "github.com/cortexlabs/cortex/pkg/operator/api/userconfig" ) var flagDeployForce bool @@ -53,21 +56,47 @@ func deploy(force bool, ignoreCache bool) { errors.Exit(err) } - zipInput := &zip.Input{ + params := map[string]string{ + "force": s.Bool(force), + "ignoreCache": s.Bool(ignoreCache), + } + + configBytes, err := ioutil.ReadFile("cortex.yaml") + if err != nil { + errors.Exit(errors.Wrap(err, "cortex.yaml", userconfig.ErrorReadConfig().Error())) + } + + projectPaths, err := files.ListDirRecursive(root, false, + files.IgnoreCortexYAML, + files.IgnoreHiddenFiles, + files.IgnoreHiddenFolders, + files.IgnorePythonGeneratedFiles, + ) + if err != nil { + errors.Exit(err) + } + + projectZipBytes, err := zip.ToMem(&zip.Input{ FileLists: []zip.FileListInput{ { - Sources: allConfigPaths(root), + Sources: projectPaths, RemovePrefix: root, }, }, + }) + + if err != nil { + errors.Exit(errors.Wrap(err, "failed to zip project folder")) } - params := map[string]string{ - "force": s.Bool(force), - "ignoreCache": s.Bool(ignoreCache), + uploadInput := &HTTPUploadInput{ + Bytes: map[string][]byte{ + "cortex.yaml": configBytes, + "project.zip": projectZipBytes, + }, } - response, err := HTTPUploadZip("/deploy", zipInput, "config.zip", params) + response, err := HTTPUpload("/deploy", uploadInput, params) if err != nil { errors.Exit(err) } diff --git a/cli/cmd/get.go b/cli/cmd/get.go index c6c92273d6..6d20aa3668 100644 --- a/cli/cmd/get.go +++ b/cli/cmd/get.go @@ -174,8 +174,6 @@ func resourceByNameStr(resourceName string, resourcesRes *schema.GetResourcesRes return "", err } switch resourceType := rs.GetResourceType(); resourceType { - case resource.PythonPackageType: - return describePythonPackage(resourceName, resourcesRes) case resource.APIType: return describeAPI(resourceName, resourcesRes, flagVerbose) default: @@ -185,8 +183,6 @@ func resourceByNameStr(resourceName string, resourcesRes *schema.GetResourcesRes func resourcesByTypeStr(resourceType resource.Type, resourcesRes *schema.GetResourcesResponse) (string, error) { switch resourceType { - case resource.PythonPackageType: - return pythonPackagesStr(resourcesRes.DataStatuses, resourcesRes.Context), nil case resource.APIType: return apisStr(resourcesRes.APIGroupStatuses), nil default: @@ -196,8 +192,6 @@ func resourcesByTypeStr(resourceType resource.Type, resourcesRes *schema.GetReso func resourceByNameAndTypeStr(resourceName string, resourceType resource.Type, resourcesRes *schema.GetResourcesResponse, flagVerbose bool) (string, error) { switch resourceType { - case resource.PythonPackageType: - return describePythonPackage(resourceName, resourcesRes) case resource.APIType: return describeAPI(resourceName, resourcesRes, flagVerbose) default: @@ -206,27 +200,11 @@ func resourceByNameAndTypeStr(resourceName string, resourceType resource.Type, r } func allResourcesStr(resourcesRes *schema.GetResourcesResponse) string { - ctx := resourcesRes.Context - out := "" - out += pythonPackagesStr(resourcesRes.DataStatuses, ctx) out += apisStr(resourcesRes.APIGroupStatuses) return out } -func pythonPackagesStr(dataStatuses map[string]*resource.DataStatus, ctx *context.Context) string { - if len(ctx.PythonPackages) == 0 { - return "" - } - - resources := make([]context.Resource, 0, len(ctx.PythonPackages)) - for _, pythonPackage := range ctx.PythonPackages { - resources = append(resources, pythonPackage) - } - - return "\n" + dataResourceTable(resources, dataStatuses, resource.PythonPackageType) + "\n" -} - func apisStr(apiGroupStatuses map[string]*resource.APIGroupStatus) string { if len(apiGroupStatuses) == 0 { return "" @@ -235,15 +213,6 @@ func apisStr(apiGroupStatuses map[string]*resource.APIGroupStatus) string { return "\n" + apiResourceTable(apiGroupStatuses) } -func describePythonPackage(name string, resourcesRes *schema.GetResourcesResponse) (string, error) { - pythonPackage := resourcesRes.Context.PythonPackages[name] - if pythonPackage == nil { - return "", userconfig.ErrorUndefinedResource(name, resource.PythonPackageType) - } - dataStatus := resourcesRes.DataStatuses[pythonPackage.GetID()] - return dataStatusSummary(dataStatus), nil -} - func describeAPI(name string, resourcesRes *schema.GetResourcesResponse, flagVerbose bool) (string, error) { groupStatus := resourcesRes.APIGroupStatuses[name] if groupStatus == nil { @@ -553,10 +522,6 @@ func dataResourceTable(resources []context.Resource, dataStatuses map[string]*re } title := resourceType.UserFacing() - if resourceType == resource.PythonPackageType { - title = resourceType.UserFacingPlural() - } - t := table.Table{ Headers: []table.Header{ {Title: title, MaxWidth: 32}, @@ -615,14 +580,8 @@ func titleStr(title string) string { } func resourceStatusesStr(resourcesRes *schema.GetResourcesResponse) string { - ctx := resourcesRes.Context var titles, values []string - if len(ctx.PythonPackages) != 0 { - titles = append(titles, resource.PythonPackageType.UserFacingPlural()) - values = append(values, pythonPackageStatusesStr(resourcesRes.DataStatuses, resourcesRes.Context)) - } - if len(resourcesRes.APIGroupStatuses) != 0 { titles = append(titles, resource.APIType.UserFacingPlural()) values = append(values, apiStatusesStr(resourcesRes.APIGroupStatuses)) @@ -643,16 +602,6 @@ func resourceStatusesStr(resourcesRes *schema.GetResourcesResponse) string { return out } -func pythonPackageStatusesStr(dataStatuses map[string]*resource.DataStatus, ctx *context.Context) string { - var statuses = make([]resource.Status, len(ctx.PythonPackages)) - i := 0 - for _, pythonPackage := range ctx.PythonPackages { - statuses[i] = dataStatuses[pythonPackage.GetID()] - i++ - } - return StatusStr(statuses) -} - func apiStatusesStr(apiGroupStatuses map[string]*resource.APIGroupStatus) string { var statuses = make([]resource.Status, len(apiGroupStatuses)) i := 0 diff --git a/cli/cmd/lib_config_reader.go b/cli/cmd/lib_config_reader.go index efa8a08457..e610a96276 100644 --- a/cli/cmd/lib_config_reader.go +++ b/cli/cmd/lib_config_reader.go @@ -20,10 +20,8 @@ import ( "os" "path/filepath" - "github.com/cortexlabs/cortex/pkg/consts" "github.com/cortexlabs/cortex/pkg/lib/errors" "github.com/cortexlabs/cortex/pkg/lib/files" - "github.com/cortexlabs/cortex/pkg/lib/sets/strset" "github.com/cortexlabs/cortex/pkg/operator/api/userconfig" ) @@ -68,27 +66,6 @@ func pythonPaths(dir string) []string { return pyPaths } -func allConfigPaths(root string) []string { - exportPaths := strset.New() - requirementsPath := filepath.Join(root, consts.RequirementsTxt) - if err := files.CheckFile(requirementsPath); err == nil { - exportPaths.Add(requirementsPath) - } - - customPackagesRoot := filepath.Join(root, consts.PackageDir) - if err := files.CheckDir(customPackagesRoot); err == nil { - customPackagesPaths, err := files.ListDirRecursive(customPackagesRoot, false, files.IgnoreHiddenFiles, files.IgnoreHiddenFolders, files.IgnorePythonGeneratedFiles) - if err != nil { - errors.Exit(err) - } - exportPaths.Add(customPackagesPaths...) - } - exportPaths.Add(yamlPaths(root)...) - exportPaths.Add(pythonPaths(root)...) - - return exportPaths.Slice() -} - func appNameFromConfig() (string, error) { appRoot := mustAppRoot() return userconfig.ReadAppName(filepath.Join(appRoot, "cortex.yaml"), "cortex.yaml") diff --git a/cortex.sh b/cortex.sh index f3f2edd634..7bf0b8b5f5 100755 --- a/cortex.sh +++ b/cortex.sh @@ -171,7 +171,6 @@ export CORTEX_IMAGE_FLUENTD="${CORTEX_IMAGE_FLUENTD:-cortexlabs/fluentd:$CORTEX_ export CORTEX_IMAGE_OPERATOR="${CORTEX_IMAGE_OPERATOR:-cortexlabs/operator:$CORTEX_VERSION_STABLE}" export CORTEX_IMAGE_TF_SERVE="${CORTEX_IMAGE_TF_SERVE:-cortexlabs/tf-serve:$CORTEX_VERSION_STABLE}" export CORTEX_IMAGE_TF_API="${CORTEX_IMAGE_TF_API:-cortexlabs/tf-api:$CORTEX_VERSION_STABLE}" -export CORTEX_IMAGE_PYTHON_PACKAGER="${CORTEX_IMAGE_PYTHON_PACKAGER:-cortexlabs/python-packager:$CORTEX_VERSION_STABLE}" export CORTEX_IMAGE_TF_SERVE_GPU="${CORTEX_IMAGE_TF_SERVE_GPU:-cortexlabs/tf-serve-gpu:$CORTEX_VERSION_STABLE}" export CORTEX_IMAGE_ONNX_SERVE="${CORTEX_IMAGE_ONNX_SERVE:-cortexlabs/onnx-serve:$CORTEX_VERSION_STABLE}" export CORTEX_IMAGE_ONNX_SERVE_GPU="${CORTEX_IMAGE_ONNX_SERVE_GPU:-cortexlabs/onnx-serve-gpu:$CORTEX_VERSION_STABLE}" @@ -231,7 +230,6 @@ function install_cortex() { -e CORTEX_IMAGE_OPERATOR=$CORTEX_IMAGE_OPERATOR \ -e CORTEX_IMAGE_TF_SERVE=$CORTEX_IMAGE_TF_SERVE \ -e CORTEX_IMAGE_TF_API=$CORTEX_IMAGE_TF_API \ - -e CORTEX_IMAGE_PYTHON_PACKAGER=$CORTEX_IMAGE_PYTHON_PACKAGER \ -e CORTEX_IMAGE_TF_SERVE_GPU=$CORTEX_IMAGE_TF_SERVE_GPU \ -e CORTEX_IMAGE_ONNX_SERVE=$CORTEX_IMAGE_ONNX_SERVE \ -e CORTEX_IMAGE_ONNX_SERVE_GPU=$CORTEX_IMAGE_ONNX_SERVE_GPU \ diff --git a/dev/registry.sh b/dev/registry.sh index c83509a58c..f486f6e313 100755 --- a/dev/registry.sh +++ b/dev/registry.sh @@ -44,7 +44,6 @@ function create_registry() { aws ecr create-repository --repository-name=cortexlabs/operator --region=$REGISTRY_REGION || true aws ecr create-repository --repository-name=cortexlabs/tf-serve --region=$REGISTRY_REGION || true aws ecr create-repository --repository-name=cortexlabs/tf-api --region=$REGISTRY_REGION || true - aws ecr create-repository --repository-name=cortexlabs/python-packager --region=$REGISTRY_REGION || true aws ecr create-repository --repository-name=cortexlabs/tf-serve-gpu --region=$REGISTRY_REGION || true aws ecr create-repository --repository-name=cortexlabs/onnx-serve --region=$REGISTRY_REGION || true aws ecr create-repository --repository-name=cortexlabs/onnx-serve-gpu --region=$REGISTRY_REGION || true @@ -129,7 +128,6 @@ elif [ "$cmd" = "update" ]; then build_and_push $ROOT/images/tf-serve tf-serve latest build_and_push $ROOT/images/tf-serve-gpu tf-serve-gpu latest build_and_push $ROOT/images/onnx-serve-gpu onnx-serve-gpu latest - build_and_push $ROOT/images/python-packager python-packager latest build_and_push $ROOT/images/cluster-autoscaler cluster-autoscaler latest build_and_push $ROOT/images/nvidia nvidia latest build_and_push $ROOT/images/metrics-server metrics-server latest diff --git a/docs/cluster/config.md b/docs/cluster/config.md index 7689b28f34..416774dfc0 100644 --- a/docs/cluster/config.md +++ b/docs/cluster/config.md @@ -51,7 +51,6 @@ export CORTEX_IMAGE_TF_API="cortexlabs/tf-api:master" export CORTEX_IMAGE_TF_SERVE_GPU="cortexlabs/tf-serve-gpu:master" export CORTEX_IMAGE_ONNX_SERVE="cortexlabs/onnx-serve:master" export CORTEX_IMAGE_ONNX_SERVE_GPU="cortexlabs/onnx-serve-gpu:master" -export CORTEX_IMAGE_PYTHON_PACKAGER="cortexlabs/python-packager:master" export CORTEX_IMAGE_CLUSTER_AUTOSCALER="cortexlabs/cluster-autoscaler:master" export CORTEX_IMAGE_NVIDIA="cortexlabs/nvidia:master" export CORTEX_IMAGE_METRICS_SERVER="cortexlabs/metrics-server:master" diff --git a/docs/cluster/development.md b/docs/cluster/development.md index 67fc1f0c12..65fbd7fb91 100644 --- a/docs/cluster/development.md +++ b/docs/cluster/development.md @@ -69,7 +69,6 @@ export CORTEX_IMAGE_OPERATOR="XXXXXXXX.dkr.ecr.us-west-2.amazonaws.com/cortexlab export CORTEX_IMAGE_TF_SERVE="XXXXXXXX.dkr.ecr.us-west-2.amazonaws.com/cortexlabs/tf-serve:latest" export CORTEX_IMAGE_TF_SERVE_GPU="XXXXXXXX.dkr.ecr.us-west-2.amazonaws.com/cortexlabs/tf-serve-gpu:latest" export CORTEX_IMAGE_TF_API="XXXXXXXX.dkr.ecr.us-west-2.amazonaws.com/cortexlabs/tf-api:latest" -export CORTEX_IMAGE_PYTHON_PACKAGER="XXXXXXXX.dkr.ecr.us-west-2.amazonaws.com/cortexlabs/python-packager:latest" export CORTEX_IMAGE_CLUSTER_AUTOSCALER="XXXXXXXX.dkr.ecr.us-west-2.amazonaws.com/cortexlabs/cluster-autoscaler:latest" export CORTEX_IMAGE_NVIDIA="XXXXXXXX.dkr.ecr.us-west-2.amazonaws.com/cortexlabs/nvidia:latest" export CORTEX_IMAGE_METRICS_SERVER="XXXXXXXX.dkr.ecr.us-west-2.amazonaws.com/cortexlabs/metrics-server:latest" diff --git a/docs/deployments/python-packages.md b/docs/deployments/python-packages.md index 85a165410f..f7c399ceb2 100644 --- a/docs/deployments/python-packages.md +++ b/docs/deployments/python-packages.md @@ -12,35 +12,3 @@ Cortex looks for a `requirements.txt` file in the top level cortex directory (in ├── ... └── requirements.txt ``` - -## Custom Packages - -Cortex looks for your Python packages in the directory `./packages/`. The package must have a `setup.py` in the root of the package directory with the name set to your package name. Cortex will run `pip3 wheel -w wheelhouse ./packages/` to construct wheels for the Python Project. - -```text -./iris/ -├── cortex.yaml -├── ... -└── packages - └── acme-util - ├── acme-util/ - | ├── util_a.py - | └── util_b.py - └── setup.py -``` - -Sample `setup.py` - -```python -from distutils.core import setup - -setup( - name="acme-util", - version="0.0.1", - description="", - author="ACME C. M. Exists", - author_email="acme@acme.com", - packages=["acme-util"], - install_requires=["pyyaml==3.13"], -) -``` diff --git a/examples/text-generator/cortex.yaml b/examples/text-generator/cortex.yaml index 6ecc0c4b62..d9727bf1b2 100644 --- a/examples/text-generator/cortex.yaml +++ b/examples/text-generator/cortex.yaml @@ -4,6 +4,6 @@ - kind: api name: generator model: s3://cortex-examples/gpt-2/774 - request_handler: encoder.py + request_handler: handler.py compute: gpu: 1 diff --git a/examples/text-generator/encoder.py b/examples/text-generator/encoder.py index 71fb12ce24..99f9b589e6 100644 --- a/examples/text-generator/encoder.py +++ b/examples/text-generator/encoder.py @@ -115,16 +115,3 @@ def get_encoder(): ) bpe_merges = [tuple(merge_str.split()) for merge_str in bpe_data.split("\n")[1:-1]] return Encoder(encoder=encoder, bpe_merges=bpe_merges) - - -encoder = get_encoder() - - -def pre_inference(sample, metadata): - context = encoder.encode(sample["text"]) - return {"context": [context]} - - -def post_inference(prediction, metadata): - response = prediction["response"]["sample"] - return {encoder.decode(response)} diff --git a/examples/text-generator/handler.py b/examples/text-generator/handler.py new file mode 100644 index 0000000000..5e3321b330 --- /dev/null +++ b/examples/text-generator/handler.py @@ -0,0 +1,13 @@ +from encoder import get_encoder + +encoder = get_encoder() + + +def pre_inference(sample, metadata): + context = encoder.encode(sample["text"]) + return {"context": [context]} + + +def post_inference(prediction, metadata): + response = prediction["response"]["sample"] + return {encoder.decode(response)} diff --git a/images/onnx-serve-gpu/Dockerfile b/images/onnx-serve-gpu/Dockerfile index 1c82f3e3a6..1172306cb5 100644 --- a/images/onnx-serve-gpu/Dockerfile +++ b/images/onnx-serve-gpu/Dockerfile @@ -26,7 +26,7 @@ RUN apt-get update -qq && apt-get install -y -q \ && apt-get clean -qq && rm -rf /var/lib/apt/lists/* -ENV PYTHONPATH="/src:${PYTHONPATH}" +ENV PYTHONPATH "${PYTHONPATH}:/src:/mnt/project" COPY pkg/workloads/cortex/lib/requirements.txt /src/cortex/lib/requirements.txt COPY pkg/workloads/cortex/onnx_serve/requirements.txt /src/cortex/onnx_serve/requirements.txt @@ -39,4 +39,4 @@ COPY pkg/workloads/cortex/consts.py /src/cortex COPY pkg/workloads/cortex/lib /src/cortex/lib COPY pkg/workloads/cortex/onnx_serve /src/cortex/onnx_serve -ENTRYPOINT ["/usr/bin/python3", "/src/cortex/onnx_serve/api.py"] +ENTRYPOINT ["/src/cortex/onnx_serve/run.sh"] diff --git a/images/onnx-serve/Dockerfile b/images/onnx-serve/Dockerfile index efb3b615c8..0f238af080 100644 --- a/images/onnx-serve/Dockerfile +++ b/images/onnx-serve/Dockerfile @@ -26,7 +26,7 @@ RUN apt-get update -qq && apt-get install -y -q \ && apt-get clean -qq && rm -rf /var/lib/apt/lists/* -ENV PYTHONPATH="/src:${PYTHONPATH}" +ENV PYTHONPATH "${PYTHONPATH}:/src:/mnt/project" COPY pkg/workloads/cortex/lib/requirements.txt /src/cortex/lib/requirements.txt COPY pkg/workloads/cortex/onnx_serve/requirements.txt /src/cortex/onnx_serve/requirements.txt @@ -39,4 +39,4 @@ COPY pkg/workloads/cortex/consts.py /src/cortex COPY pkg/workloads/cortex/lib /src/cortex/lib COPY pkg/workloads/cortex/onnx_serve /src/cortex/onnx_serve -ENTRYPOINT ["/usr/bin/python3", "/src/cortex/onnx_serve/api.py"] +ENTRYPOINT ["/src/cortex/onnx_serve/run.sh"] diff --git a/images/python-packager/Dockerfile b/images/python-packager/Dockerfile deleted file mode 100644 index 928de952f5..0000000000 --- a/images/python-packager/Dockerfile +++ /dev/null @@ -1,37 +0,0 @@ -FROM ubuntu:18.04 - -RUN apt-get update -qq && apt-get install -y -q \ - python3 \ - python3-dev \ - python3-pip \ - && apt-get clean -qq && rm -rf /var/lib/apt/lists/* && \ - pip3 install --upgrade \ - pip \ - setuptools \ - && rm -rf /root/.cache/pip* - -RUN apt-get update -qq && apt-get install -y -q \ - build-essential \ - curl \ - libfreetype6-dev \ - libpng-dev \ - libzmq3-dev \ - pkg-config \ - rsync \ - software-properties-common \ - unzip \ - zlib1g-dev \ - && apt-get clean -qq && rm -rf /var/lib/apt/lists/* - -COPY pkg/workloads/cortex/consts.py /src/cortex/ -COPY pkg/workloads/cortex/lib /src/cortex/lib - -COPY pkg/workloads/cortex/tf_api/requirements.txt /src/cortex/tf_api/requirements.txt - -RUN pip3 install -r /src/cortex/lib/requirements.txt && \ - pip3 install -r /src/cortex/tf_api/requirements.txt && \ - rm -rf /root/.cache/pip* - -ENV PYTHONPATH="/src:${PYTHONPATH}" - -ENTRYPOINT ["/usr/bin/python3", "/src/cortex/lib/package.py"] diff --git a/images/tf-api/Dockerfile b/images/tf-api/Dockerfile index d3115fd473..c0c7843b24 100644 --- a/images/tf-api/Dockerfile +++ b/images/tf-api/Dockerfile @@ -4,7 +4,7 @@ RUN apt-get update -qq && apt-get install -y -q \ zlib1g-dev \ && apt-get clean -qq && rm -rf /var/lib/apt/lists/* -ENV PYTHONPATH="/src:${PYTHONPATH}" +ENV PYTHONPATH "${PYTHONPATH}:/src:/mnt/project" RUN apt-get update -qq && apt-get install -y -q \ build-essential \ libfreetype6-dev \ @@ -25,4 +25,4 @@ COPY pkg/workloads/cortex/consts.py /src/cortex/ COPY pkg/workloads/cortex/lib /src/cortex/lib COPY pkg/workloads/cortex/tf_api /src/cortex/tf_api -ENTRYPOINT ["/usr/bin/python3", "/src/cortex/tf_api/api.py"] +ENTRYPOINT ["/src/cortex/tf_api/run.sh"] diff --git a/manager/install_cortex.sh b/manager/install_cortex.sh index 1a171ea31d..93f96c5fbc 100755 --- a/manager/install_cortex.sh +++ b/manager/install_cortex.sh @@ -65,7 +65,6 @@ function setup_configmap() { --from-literal='IMAGE_ONNX_SERVE_GPU'=$CORTEX_IMAGE_ONNX_SERVE_GPU \ --from-literal='IMAGE_TF_API'=$CORTEX_IMAGE_TF_API \ --from-literal='IMAGE_DOWNLOADER'=$CORTEX_IMAGE_DOWNLOADER \ - --from-literal='IMAGE_PYTHON_PACKAGER'=$CORTEX_IMAGE_PYTHON_PACKAGER \ --from-literal='IMAGE_TF_SERVE_GPU'=$CORTEX_IMAGE_TF_SERVE_GPU \ --from-literal='ENABLE_TELEMETRY'=$CORTEX_ENABLE_TELEMETRY \ -o yaml --dry-run | kubectl apply -f - >/dev/null diff --git a/pkg/consts/consts.go b/pkg/consts/consts.go index 6bb9c1875d..1d08176e31 100644 --- a/pkg/consts/consts.go +++ b/pkg/consts/consts.go @@ -26,14 +26,11 @@ var ( CortexConfigPath = "/configs/cortex" CortexConfigName = "cortex-config" - RequirementsTxt = "requirements.txt" - PackageDir = "packages" - AppsDir = "apps" DeploymentsDir = "deployments" APIsDir = "apis" - PythonPackagesDir = "python_packages" RequestHandlersDir = "request_handlers" + ProjectsDir = "projects" ContextsDir = "contexts" ResourceStatusesDir = "resource_statuses" WorkloadSpecsDir = "workload_specs" diff --git a/pkg/lib/files/files.go b/pkg/lib/files/files.go index dea4de0b0e..830987bb66 100644 --- a/pkg/lib/files/files.go +++ b/pkg/lib/files/files.go @@ -241,6 +241,13 @@ func IgnoreHiddenFiles(path string, fi os.FileInfo) (bool, error) { return false, nil } +func IgnoreCortexYAML(path string, fi os.FileInfo) (bool, error) { + if !fi.IsDir() && fi.Name() == "cortex.yaml" { + return true, nil + } + return false, nil +} + func IgnoreHiddenFolders(path string, fi os.FileInfo) (bool, error) { if fi.IsDir() && strings.HasPrefix(fi.Name(), ".") { return true, nil diff --git a/pkg/operator/api/context/apis.go b/pkg/operator/api/context/apis.go index 2f4d47c074..9fcf247a72 100644 --- a/pkg/operator/api/context/apis.go +++ b/pkg/operator/api/context/apis.go @@ -25,8 +25,7 @@ type APIs map[string]*API type API struct { *userconfig.API *ComputedResourceFields - Path string `json:"path"` - RequestHandlerImplKey *string `json:"request_handler_impl_key"` + Path string `json:"path"` } func APIPath(apiName string, appName string) string { diff --git a/pkg/operator/api/context/context.go b/pkg/operator/api/context/context.go index 5bc767fd70..f073679f49 100644 --- a/pkg/operator/api/context/context.go +++ b/pkg/operator/api/context/context.go @@ -36,8 +36,9 @@ type Context struct { MetadataRoot string `json:"metadata_root"` StatusPrefix string `json:"status_prefix"` App *App `json:"app"` - PythonPackages PythonPackages `json:"python_packages"` APIs APIs `json:"apis"` + ProjectID string `json:"project_id"` + ProjectKey string `json:"project_key"` } type Resource interface { @@ -83,9 +84,6 @@ func ExtractResourceWorkloadIDs(resources []ComputedResource) map[string]string func (ctx *Context) DataComputedResources() []ComputedResource { var resources []ComputedResource - for _, pythonPackage := range ctx.PythonPackages { - resources = append(resources, pythonPackage) - } return resources } @@ -170,9 +168,6 @@ func (ctx *Context) CheckAllWorkloadIDsPopulated() error { func (ctx *Context) VisibleResourcesMap() map[string][]ComputedResource { resources := make(map[string][]ComputedResource) - for name, pythonPackage := range ctx.PythonPackages { - resources[name] = append(resources[name], pythonPackage) - } for name, api := range ctx.APIs { resources[name] = append(resources[name], api) } @@ -203,12 +198,6 @@ func (ctx *Context) VisibleResourceByNameAndType(name string, resourceTypeStr st resourceType := resource.TypeFromString(resourceTypeStr) switch resourceType { - case resource.PythonPackageType: - res := ctx.PythonPackages[name] - if res == nil { - return nil, resource.ErrorNotFound(name, resourceType) - } - return res, nil case resource.APIType: res := ctx.APIs[name] if res == nil { diff --git a/pkg/operator/api/context/dependencies.go b/pkg/operator/api/context/dependencies.go index dee7b60835..6e3cec3df2 100644 --- a/pkg/operator/api/context/dependencies.go +++ b/pkg/operator/api/context/dependencies.go @@ -44,11 +44,6 @@ func (ctx *Context) allComputedResourceDependenciesHelper(resourceID string, all func (ctx *Context) DirectComputedResourceDependencies(resourceIDs ...string) strset.Set { allDependencies := strset.New() for _, resourceID := range resourceIDs { - for _, pythonPackage := range ctx.PythonPackages { - if pythonPackage.GetID() == resourceID { - allDependencies.Merge(ctx.pythonPackageDependencies(pythonPackage)) - } - } for _, api := range ctx.APIs { if api.ID == resourceID { allDependencies.Merge(ctx.apiDependencies(api)) @@ -59,17 +54,7 @@ func (ctx *Context) DirectComputedResourceDependencies(resourceIDs ...string) st return allDependencies } -func (ctx *Context) pythonPackageDependencies(pythonPackage *PythonPackage) strset.Set { - return strset.New() -} - func (ctx *Context) apiDependencies(api *API) strset.Set { dependencies := strset.New() - - if api.RequestHandler != nil { - for _, pythonPackage := range ctx.PythonPackages { - dependencies.Add(pythonPackage.GetID()) - } - } return dependencies } diff --git a/pkg/operator/api/context/python_packages.go b/pkg/operator/api/context/python_packages.go deleted file mode 100644 index 4b5627c7c9..0000000000 --- a/pkg/operator/api/context/python_packages.go +++ /dev/null @@ -1,35 +0,0 @@ -/* -Copyright 2019 Cortex Labs, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package context - -import ( - "github.com/cortexlabs/cortex/pkg/operator/api/resource" - "github.com/cortexlabs/cortex/pkg/operator/api/userconfig" -) - -type PythonPackages map[string]*PythonPackage - -type PythonPackage struct { - userconfig.ResourceFields - *ComputedResourceFields - SrcKey string `json:"src_key"` - PackageKey string `json:"package_key"` -} - -func (pythonPackage *PythonPackage) GetResourceType() resource.Type { - return resource.PythonPackageType -} diff --git a/pkg/operator/api/resource/type.go b/pkg/operator/api/resource/type.go index a729b1b404..87aa3ffb01 100644 --- a/pkg/operator/api/resource/type.go +++ b/pkg/operator/api/resource/type.go @@ -24,10 +24,9 @@ type Type int type Types []Type const ( - UnknownType Type = iota // 0 - AppType // 1 - APIType // 2 - PythonPackageType // 3 + UnknownType Type = iota // 0 + AppType // 1 + APIType // 2 ) var ( @@ -35,41 +34,31 @@ var ( "unknown", "deployment", "api", - "python_package", } typePlurals = []string{ "unknown", "deployments", "apis", - "python_packages", } userFacing = []string{ "unknown", "deployment", "api", - "python package", } userFacingPlural = []string{ "unknowns", "deployments", "apis", - "python packages", - } - - typeAcronyms = map[string]Type{ - "py": PythonPackageType, - "pys": PythonPackageType, - "pp": PythonPackageType, - "pps": PythonPackageType, } VisibleTypes = Types{ APIType, - PythonPackageType, } + + typeAcronyms = map[string]Type{} ) func TypeFromString(s string) Type { diff --git a/pkg/operator/config/config.go b/pkg/operator/config/config.go index 96778b4d9f..d474f8cab4 100644 --- a/pkg/operator/config/config.go +++ b/pkg/operator/config/config.go @@ -37,20 +37,19 @@ var ( ) type CortexConfig struct { - ID string `json:"id"` - APIVersion string `json:"api_version"` - Bucket string `json:"bucket"` - LogGroup string `json:"log_group"` - Region string `json:"region"` - Namespace string `json:"namespace"` - OperatorImage string `json:"operator_image"` - TFServeImage string `json:"tf_serve_image"` - TFAPIImage string `json:"tf_api_image"` - DownloaderImage string `json:"downloader_image"` - PythonPackagerImage string `json:"python_packager_image"` - TFServeImageGPU string `json:"tf_serve_image_gpu"` - ONNXServeImage string `json:"onnx_serve_image"` - ONNXServeImageGPU string `json:"onnx_serve_gpu_image"` + ID string `json:"id"` + APIVersion string `json:"api_version"` + Bucket string `json:"bucket"` + LogGroup string `json:"log_group"` + Region string `json:"region"` + Namespace string `json:"namespace"` + OperatorImage string `json:"operator_image"` + TFServeImage string `json:"tf_serve_image"` + TFAPIImage string `json:"tf_api_image"` + DownloaderImage string `json:"downloader_image"` + TFServeImageGPU string `json:"tf_serve_image_gpu"` + ONNXServeImage string `json:"onnx_serve_image"` + ONNXServeImageGPU string `json:"onnx_serve_gpu_image"` TelemetryURL string `json:"telemetry_url"` EnableTelemetry bool `json:"enable_telemetry"` @@ -59,19 +58,18 @@ type CortexConfig struct { func Init() error { Cortex = &CortexConfig{ - APIVersion: consts.CortexVersion, - Bucket: getStr("BUCKET"), - LogGroup: getStr("LOG_GROUP"), - Region: getStr("REGION"), - Namespace: getStr("NAMESPACE"), - OperatorImage: getStr("IMAGE_OPERATOR"), - TFServeImage: getStr("IMAGE_TF_SERVE"), - TFAPIImage: getStr("IMAGE_TF_API"), - DownloaderImage: getStr("IMAGE_DOWNLOADER"), - PythonPackagerImage: getStr("IMAGE_PYTHON_PACKAGER"), - TFServeImageGPU: getStr("IMAGE_TF_SERVE_GPU"), - ONNXServeImage: getStr("IMAGE_ONNX_SERVE"), - ONNXServeImageGPU: getStr("IMAGE_ONNX_SERVE_GPU"), + APIVersion: consts.CortexVersion, + Bucket: getStr("BUCKET"), + LogGroup: getStr("LOG_GROUP"), + Region: getStr("REGION"), + Namespace: getStr("NAMESPACE"), + OperatorImage: getStr("IMAGE_OPERATOR"), + TFServeImage: getStr("IMAGE_TF_SERVE"), + TFAPIImage: getStr("IMAGE_TF_API"), + DownloaderImage: getStr("IMAGE_DOWNLOADER"), + TFServeImageGPU: getStr("IMAGE_TF_SERVE_GPU"), + ONNXServeImage: getStr("IMAGE_ONNX_SERVE"), + ONNXServeImageGPU: getStr("IMAGE_ONNX_SERVE_GPU"), TelemetryURL: configreader.MustStringFromEnv("CORTEX_TELEMETRY_URL", &configreader.StringValidation{Required: false, Default: consts.TelemetryURL}), EnableTelemetry: getBool("ENABLE_TELEMETRY", false), diff --git a/pkg/operator/context/apis.go b/pkg/operator/context/apis.go index 16fea5cfe5..1cb0c1698b 100644 --- a/pkg/operator/context/apis.go +++ b/pkg/operator/context/apis.go @@ -18,58 +18,25 @@ package context import ( "bytes" - "path/filepath" "strings" - "github.com/cortexlabs/cortex/pkg/consts" - "github.com/cortexlabs/cortex/pkg/lib/errors" "github.com/cortexlabs/cortex/pkg/lib/hash" - "github.com/cortexlabs/cortex/pkg/lib/pointer" - "github.com/cortexlabs/cortex/pkg/lib/sets/strset" s "github.com/cortexlabs/cortex/pkg/lib/strings" "github.com/cortexlabs/cortex/pkg/operator/api/context" "github.com/cortexlabs/cortex/pkg/operator/api/resource" "github.com/cortexlabs/cortex/pkg/operator/api/userconfig" - "github.com/cortexlabs/cortex/pkg/operator/config" ) -var uploadedRequestHandlers = strset.New() - -func getAPIs(config *userconfig.Config, - deploymentVersion string, - impls map[string][]byte, - pythonPackages context.PythonPackages, -) (context.APIs, error) { +func getAPIs(config *userconfig.Config, deploymentVersion string, projectID string) (context.APIs, error) { apis := context.APIs{} for _, apiConfig := range config.APIs { var buf bytes.Buffer - var requestHandlerImplKey *string buf.WriteString(apiConfig.Name) buf.WriteString(s.Obj(apiConfig.Tracker)) buf.WriteString(apiConfig.ModelFormat.String()) - - if apiConfig.RequestHandler != nil { - for _, pythonPackage := range pythonPackages { - buf.WriteString(pythonPackage.GetID()) - } - - impl, ok := impls[*apiConfig.RequestHandler] - if !ok { - return nil, errors.Wrap(userconfig.ErrorImplDoesNotExist(*apiConfig.RequestHandler), userconfig.Identify(apiConfig), userconfig.RequestHandlerKey) - } - implID := hash.Bytes(impl) - buf.WriteString(implID) - - requestHandlerImplKey = pointer.String(filepath.Join(consts.RequestHandlersDir, implID)) - - err := uploadRequestHandler(*requestHandlerImplKey, impls[*apiConfig.RequestHandler]) - if err != nil { - return nil, errors.Wrap(err, userconfig.Identify(apiConfig)) - } - } - buf.WriteString(deploymentVersion) + buf.WriteString(projectID) buf.WriteString(strings.TrimSuffix(apiConfig.Model, "/")) id := hash.Bytes(buf.Bytes()) @@ -81,27 +48,9 @@ func getAPIs(config *userconfig.Config, ResourceType: resource.APIType, }, }, - API: apiConfig, - Path: context.APIPath(apiConfig.Name, config.App.Name), - RequestHandlerImplKey: requestHandlerImplKey, + API: apiConfig, + Path: context.APIPath(apiConfig.Name, config.App.Name), } } return apis, nil } - -func uploadRequestHandler(implKey string, impl []byte) error { - isUploaded, err := config.AWS.IsS3File(implKey) - if err != nil { - return errors.Wrap(err, "upload") - } - - if !isUploaded { - err = config.AWS.UploadBytesToS3(impl, implKey) - if err != nil { - return errors.Wrap(err, "upload") - } - } - - uploadedRequestHandlers.Add(implKey) - return nil -} diff --git a/pkg/operator/context/context.go b/pkg/operator/context/context.go index f005a37d2d..0ecc1dd0ce 100644 --- a/pkg/operator/context/context.go +++ b/pkg/operator/context/context.go @@ -31,10 +31,9 @@ import ( func New( userconf *userconfig.Config, - files map[string][]byte, + projectBytes []byte, ignoreCache bool, ) (*context.Context, error) { - ctx := &context.Context{} ctx.CreatedEpoch = time.Now().Unix() @@ -60,15 +59,14 @@ func New( consts.MetadataDir, ) - ctx.StatusPrefix = StatusPrefix(ctx.App.Name) - - pythonPackages, err := loadPythonPackages(files, ctx.DeploymentVersion) - if err != nil { + ctx.ProjectID = hash.Bytes(projectBytes) + ctx.ProjectKey = filepath.Join(consts.ProjectsDir, ctx.ProjectID+".zip") + if err = config.AWS.UploadBytesToS3(projectBytes, ctx.ProjectKey); err != nil { return nil, err } - ctx.PythonPackages = pythonPackages - apis, err := getAPIs(userconf, ctx.DeploymentVersion, files, pythonPackages) + ctx.StatusPrefix = statusPrefix(ctx.App.Name) + apis, err := getAPIs(userconf, ctx.DeploymentVersion, ctx.ProjectID) if err != nil { return nil, err @@ -101,6 +99,7 @@ func calculateID(ctx *context.Context) string { ids = append(ids, ctx.Root) ids = append(ids, ctx.StatusPrefix) ids = append(ids, ctx.App.ID) + ids = append(ids, ctx.ProjectID) for _, resource := range ctx.AllResources() { ids = append(ids, resource.GetID()) @@ -121,7 +120,7 @@ func DownloadContext(ctxID string, appName string) (*context.Context, error) { return &ctx, nil } -func StatusPrefix(appName string) string { +func statusPrefix(appName string) string { return filepath.Join( consts.AppsDir, appName, @@ -131,7 +130,7 @@ func StatusPrefix(appName string) string { func StatusKey(resourceID string, workloadID string, appName string) string { return filepath.Join( - StatusPrefix(appName), + statusPrefix(appName), resourceID, workloadID, ) @@ -139,7 +138,7 @@ func StatusKey(resourceID string, workloadID string, appName string) string { func LatestWorkloadIDKey(resourceID string, appName string) string { return filepath.Join( - StatusPrefix(appName), + statusPrefix(appName), resourceID, "latest", ) diff --git a/pkg/operator/context/python_packages.go b/pkg/operator/context/python_packages.go deleted file mode 100644 index 0b92675248..0000000000 --- a/pkg/operator/context/python_packages.go +++ /dev/null @@ -1,125 +0,0 @@ -/* -Copyright 2019 Cortex Labs, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package context - -import ( - "bytes" - "path/filepath" - "strings" - - "github.com/cortexlabs/cortex/pkg/consts" - "github.com/cortexlabs/cortex/pkg/lib/errors" - "github.com/cortexlabs/cortex/pkg/lib/hash" - "github.com/cortexlabs/cortex/pkg/lib/zip" - "github.com/cortexlabs/cortex/pkg/operator/api/context" - "github.com/cortexlabs/cortex/pkg/operator/api/resource" - "github.com/cortexlabs/cortex/pkg/operator/api/userconfig" - "github.com/cortexlabs/cortex/pkg/operator/config" -) - -func findCustomPackages(files map[string][]byte) []string { - var customPackages []string - for filePath := range files { - if strings.HasSuffix(filePath, "setup.py") { - packageFolder, packageName := filepath.Split(filepath.Dir(filePath)) - baseDir := filepath.Dir(packageFolder) - - if strings.TrimPrefix(baseDir, "/") == consts.PackageDir { - customPackages = append(customPackages, packageName) - } - } - } - - return customPackages -} - -func loadPythonPackages(files map[string][]byte, deploymentVersion string) (context.PythonPackages, error) { - pythonPackages := make(map[string]*context.PythonPackage) - - if reqFileBytes, ok := files[consts.RequirementsTxt]; ok { - var buf bytes.Buffer - buf.Write(reqFileBytes) - buf.WriteString(deploymentVersion) - id := hash.Bytes(buf.Bytes()) - pythonPackage := context.PythonPackage{ - ResourceFields: userconfig.ResourceFields{ - Name: consts.RequirementsTxt, - }, - ComputedResourceFields: &context.ComputedResourceFields{ - ResourceFields: &context.ResourceFields{ - ID: id, - ResourceType: resource.PythonPackageType, - }, - }, - SrcKey: filepath.Join(consts.PythonPackagesDir, id, "src.txt"), - PackageKey: filepath.Join(consts.PythonPackagesDir, id, "package.zip"), - } - - if err := config.AWS.UploadBytesToS3(reqFileBytes, pythonPackage.SrcKey); err != nil { - return nil, errors.Wrap(err, "upload", "requirements") - } - - pythonPackages[pythonPackage.Name] = &pythonPackage - } - - customPackages := findCustomPackages(files) - - for _, packageName := range customPackages { - zipBytesInputs := []zip.BytesInput{} - var buf bytes.Buffer - buf.WriteString(deploymentVersion) - for filePath, fileBytes := range files { - if strings.HasPrefix(filePath, filepath.Join(consts.PackageDir, packageName)) { - buf.Write(fileBytes) - zipBytesInputs = append(zipBytesInputs, zip.BytesInput{ - Content: fileBytes, - Dest: filePath[len(consts.PackageDir):], - }) - } - } - id := hash.Bytes(buf.Bytes()) - pythonPackage := context.PythonPackage{ - ResourceFields: userconfig.ResourceFields{ - Name: packageName, - }, - ComputedResourceFields: &context.ComputedResourceFields{ - ResourceFields: &context.ResourceFields{ - ID: id, - ResourceType: resource.PythonPackageType, - }, - }, - SrcKey: filepath.Join(consts.PythonPackagesDir, id, "src.zip"), - PackageKey: filepath.Join(consts.PythonPackagesDir, id, "package.zip"), - } - - zipInput := zip.Input{ - Bytes: zipBytesInputs, - } - - zipBytes, err := zip.ToMem(&zipInput) - if err != nil { - return nil, errors.Wrap(err, "zip", packageName) - } - - if err := config.AWS.UploadBytesToS3(zipBytes, pythonPackage.SrcKey); err != nil { - return nil, errors.Wrap(err, "upload", packageName) - } - - pythonPackages[pythonPackage.Name] = &pythonPackage - } - return pythonPackages, nil -} diff --git a/pkg/operator/endpoints/deploy.go b/pkg/operator/endpoints/deploy.go index 2c28bb5283..00d3279916 100644 --- a/pkg/operator/endpoints/deploy.go +++ b/pkg/operator/endpoints/deploy.go @@ -21,7 +21,6 @@ import ( "github.com/cortexlabs/cortex/pkg/lib/errors" "github.com/cortexlabs/cortex/pkg/lib/files" - "github.com/cortexlabs/cortex/pkg/lib/zip" "github.com/cortexlabs/cortex/pkg/operator/api/context" "github.com/cortexlabs/cortex/pkg/operator/api/resource" "github.com/cortexlabs/cortex/pkg/operator/api/schema" @@ -37,7 +36,40 @@ func Deploy(w http.ResponseWriter, r *http.Request) { ignoreCache := getOptionalBoolQParam("ignoreCache", false, r) force := getOptionalBoolQParam("force", false, r) - ctx, err := getContext(r, ignoreCache) + configBytes, err := files.ReadReqFile(r, "cortex.yaml") + if err != nil { + RespondError(w, errors.WithStack(err)) + return + } + + if len(configBytes) == 0 { + RespondError(w, ErrorFormFileMustBeProvided("cortex.yaml")) + return + } + + userconf, err := userconfig.New("cortex.yaml", configBytes, true) + if err != nil { + RespondError(w, err) + return + } + + projectBytes, err := files.ReadReqFile(r, "project.zip") + if err != nil { + RespondError(w, errors.WithStack(err)) + return + } + + if len(projectBytes) == 0 { + RespondError(w, ErrorFormFileMustBeProvided("project.zip")) + return + } + + ctx, err := ocontext.New(userconf, projectBytes, ignoreCache) + if err != nil { + RespondError(w, err) + return + } + if err != nil { RespondError(w, err) return @@ -72,11 +104,11 @@ func Deploy(w http.ResponseWriter, r *http.Request) { if isUpdating { if fullCtxMatch { - respondDeploy(w, ResDeploymentUpToDateUpdating) + Respond(w, schema.DeployResponse{Message: ResDeploymentUpToDateUpdating}) return } if !force { - respondDeploy(w, ResDifferentDeploymentUpdating) + Respond(w, schema.DeployResponse{Message: ResDifferentDeploymentUpdating}) return } } @@ -95,50 +127,18 @@ func Deploy(w http.ResponseWriter, r *http.Request) { switch { case isUpdating && ignoreCache: - respondDeploy(w, ResCachedDeletedDeploymentStarted) + Respond(w, schema.DeployResponse{Message: ResCachedDeletedDeploymentStarted}) case isUpdating && !ignoreCache: - respondDeploy(w, ResDeploymentUpdated) + Respond(w, schema.DeployResponse{Message: ResDeploymentUpdated}) case !isUpdating && ignoreCache: - respondDeploy(w, ResCachedDeletedDeploymentStarted) + Respond(w, schema.DeployResponse{Message: ResCachedDeletedDeploymentStarted}) case !isUpdating && !ignoreCache && existingCtx == nil: - respondDeploy(w, ResDeploymentStarted) + Respond(w, schema.DeployResponse{Message: ResDeploymentStarted}) case !isUpdating && !ignoreCache && existingCtx != nil && !fullCtxMatch: - respondDeploy(w, ResDeploymentUpdated) + Respond(w, schema.DeployResponse{Message: ResDeploymentUpdated}) case !isUpdating && !ignoreCache && existingCtx != nil && fullCtxMatch: - respondDeploy(w, ResDeploymentUpToDate) + Respond(w, schema.DeployResponse{Message: ResDeploymentUpToDate}) default: - respondDeploy(w, ResDeploymentUpdated) // unexpected - } -} - -func respondDeploy(w http.ResponseWriter, message string) { - response := schema.DeployResponse{Message: message} - Respond(w, response) -} - -func getContext(r *http.Request, ignoreCache bool) (*context.Context, error) { - zipBytes, err := files.ReadReqFile(r, "config.zip") - if err != nil { - return nil, errors.WithStack(err) - } - if len(zipBytes) == 0 { - return nil, ErrorFormFileMustBeProvided("config.zip") + Respond(w, schema.DeployResponse{Message: ResDeploymentUpdated}) } - - zipContents, err := zip.UnzipMemToMem(zipBytes) - if err != nil { - return nil, errors.Wrap(err, "form file", "config.zip") - } - - config, err := userconfig.New("cortex.yaml", zipContents["cortex.yaml"], true) - if err != nil { - return nil, err - } - - ctx, err := ocontext.New(config, zipContents, ignoreCache) - if err != nil { - return nil, err - } - - return ctx, nil } diff --git a/pkg/operator/workloads/api_workload.go b/pkg/operator/workloads/api_workload.go index 1ee0af9a6c..f34e251634 100644 --- a/pkg/operator/workloads/api_workload.go +++ b/pkg/operator/workloads/api_workload.go @@ -17,8 +17,11 @@ limitations under the License. package workloads import ( + "encoding/base64" "path" + "github.com/cortexlabs/cortex/pkg/lib/json" + kapps "k8s.io/api/apps/v1" kcore "k8s.io/api/core/v1" kresource "k8s.io/apimachinery/pkg/api/resource" @@ -223,6 +226,12 @@ func (aw *APIWorkload) IsFailed(ctx *context.Context) (bool, error) { return false, nil } +type downloadContainerArg struct { + From string `json:"from"` + To string `json:"to"` + Unzip bool `json:"unzip"` +} + func tfAPISpec( ctx *context.Context, api *context.API, @@ -250,6 +259,20 @@ func tfAPISpec( tfServingLimitsList["nvidia.com/gpu"] = *kresource.NewQuantity(api.Compute.GPU, kresource.DecimalSI) } + downloadArgs := []downloadContainerArg{ + { + From: ctx.APIs[api.Name].Model, + To: path.Join(consts.EmptyDirMountPath, "model"), + }, + { + From: config.AWS.S3Path(ctx.ProjectKey), + To: path.Join(consts.EmptyDirMountPath, "project"), + Unzip: true, + }, + } + + downloadArgsBytes, _ := json.Marshal(downloadArgs) + downloadArgsStr := base64.URLEncoding.EncodeToString(downloadArgsBytes) return k8s.Deployment(&k8s.DeploymentSpec{ Name: internalAPIName(api.Name, ctx.App.Name), Replicas: desiredReplicas, @@ -285,8 +308,7 @@ func tfAPISpec( Image: config.Cortex.DownloaderImage, ImagePullPolicy: "Always", Args: []string{ - "--download_from=" + ctx.APIs[api.Name].Model, - "--download_to=" + path.Join(consts.EmptyDirMountPath, "model"), + "--download=" + downloadArgsStr, }, Env: k8s.AWSCredentials(), VolumeMounts: k8s.DefaultVolumeMounts(), @@ -305,6 +327,7 @@ func tfAPISpec( "--api=" + ctx.APIs[api.Name].ID, "--model-dir=" + path.Join(consts.EmptyDirMountPath, "model"), "--cache-dir=" + consts.ContextCacheDir, + "--project-dir=" + path.Join(consts.EmptyDirMountPath, "project"), }, Env: k8s.AWSCredentials(), VolumeMounts: k8s.DefaultVolumeMounts(), @@ -396,6 +419,20 @@ func onnxAPISpec( resourceLimitsList["nvidia.com/gpu"] = *kresource.NewQuantity(api.Compute.GPU, kresource.DecimalSI) } + downloadArgs := []downloadContainerArg{ + { + From: ctx.APIs[api.Name].Model, + To: path.Join(consts.EmptyDirMountPath, "model"), + }, + { + From: config.AWS.S3Path(ctx.ProjectKey), + To: path.Join(consts.EmptyDirMountPath, "project"), + Unzip: true, + }, + } + + downloadArgsBytes, _ := json.Marshal(downloadArgs) + downloadArgsStr := base64.URLEncoding.EncodeToString(downloadArgsBytes) return k8s.Deployment(&k8s.DeploymentSpec{ Name: internalAPIName(api.Name, ctx.App.Name), Replicas: desiredReplicas, @@ -430,8 +467,7 @@ func onnxAPISpec( Image: config.Cortex.DownloaderImage, ImagePullPolicy: "Always", Args: []string{ - "--download_from=" + ctx.APIs[api.Name].Model, - "--download_to=" + path.Join(consts.EmptyDirMountPath, "model"), + "--download=" + downloadArgsStr, }, Env: k8s.AWSCredentials(), VolumeMounts: k8s.DefaultVolumeMounts(), @@ -449,6 +485,7 @@ func onnxAPISpec( "--api=" + ctx.APIs[api.Name].ID, "--model-dir=" + path.Join(consts.EmptyDirMountPath, "model"), "--cache-dir=" + consts.ContextCacheDir, + "--project-dir=" + path.Join(consts.EmptyDirMountPath, "project"), }, Env: k8s.AWSCredentials(), VolumeMounts: k8s.DefaultVolumeMounts(), diff --git a/pkg/operator/workloads/python_package_workload.go b/pkg/operator/workloads/python_package_workload.go deleted file mode 100644 index 3a8a18358b..0000000000 --- a/pkg/operator/workloads/python_package_workload.go +++ /dev/null @@ -1,132 +0,0 @@ -/* -Copyright 2019 Cortex Labs, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package workloads - -import ( - "strings" - - kcore "k8s.io/api/core/v1" - - "github.com/cortexlabs/cortex/pkg/consts" - "github.com/cortexlabs/cortex/pkg/lib/k8s" - "github.com/cortexlabs/cortex/pkg/operator/api/context" - "github.com/cortexlabs/cortex/pkg/operator/config" -) - -type PythonPackagesWorkload struct { - BaseWorkload -} - -func populatePythonPackageWorkloadIDs(ctx *context.Context, latestResourceWorkloadIDs map[string]string) { - pythonPackagesWorkloadID := generateWorkloadID() - - for _, pythonPackage := range ctx.PythonPackages { - if pythonPackage.WorkloadID != "" { - continue - } - if workloadID := latestResourceWorkloadIDs[pythonPackage.ID]; workloadID != "" { - pythonPackage.WorkloadID = workloadID - continue - } - pythonPackage.WorkloadID = pythonPackagesWorkloadID - } -} - -func extractPythonPackageWorkloads(ctx *context.Context) []Workload { - workloadMap := make(map[string]*PythonPackagesWorkload) - for _, pythonPackage := range ctx.PythonPackages { - if _, ok := workloadMap[pythonPackage.WorkloadID]; !ok { - workloadMap[pythonPackage.WorkloadID] = &PythonPackagesWorkload{ - emptyBaseWorkload(ctx.App.Name, pythonPackage.WorkloadID, workloadTypePythonPackager), - } - } - workloadMap[pythonPackage.WorkloadID].AddResource(pythonPackage) - } - - workloads := make([]Workload, 0, len(workloadMap)) - for _, workload := range workloadMap { - workloads = append(workloads, workload) - } - return workloads -} - -func (pyw *PythonPackagesWorkload) Start(ctx *context.Context) error { - spec := &k8s.JobSpec{ - Name: pyw.WorkloadID, - Labels: map[string]string{ - "appName": ctx.App.Name, - "workloadType": workloadTypePythonPackager, - "workloadID": pyw.WorkloadID, - }, - PodSpec: k8s.PodSpec{ - Labels: map[string]string{ - "appName": ctx.App.Name, - "workloadType": workloadTypePythonPackager, - "workloadID": pyw.WorkloadID, - "userFacing": "true", - }, - K8sPodSpec: kcore.PodSpec{ - RestartPolicy: "Never", - Containers: []kcore.Container{ - { - Name: "python-packager", - Image: config.Cortex.PythonPackagerImage, - ImagePullPolicy: kcore.PullAlways, - Args: []string{ - "--workload-id=" + pyw.WorkloadID, - "--context=" + config.AWS.S3Path(ctx.Key), - "--cache-dir=" + consts.ContextCacheDir, - "--python-packages=" + strings.Join(pyw.GetResourceIDs().Slice(), ","), - "--build", - }, - Env: k8s.AWSCredentials(), - VolumeMounts: k8s.DefaultVolumeMounts(), - }, - }, - Volumes: k8s.DefaultVolumes(), - ServiceAccountName: "default", - }, - }, - Namespace: config.Cortex.Namespace, - } - - _, err := config.Kubernetes.CreateJob(k8s.Job(spec)) - if err != nil { - return err - } - return nil -} - -func (pyw *PythonPackagesWorkload) IsStarted(ctx *context.Context) (bool, error) { - return config.Kubernetes.JobExists(pyw.WorkloadID) -} - -func (pyw *PythonPackagesWorkload) IsRunning(ctx *context.Context) (bool, error) { - return config.Kubernetes.IsJobRunning(pyw.WorkloadID) -} - -func (pyw *PythonPackagesWorkload) CanRun(ctx *context.Context) (bool, error) { - return areAllDataDependenciesSucceeded(ctx, pyw.GetResourceIDs()) -} - -func (pyw *PythonPackagesWorkload) IsSucceeded(ctx *context.Context) (bool, error) { - return areAllDataResourcesSucceeded(ctx, pyw.GetResourceIDs()) -} - -func (pyw *PythonPackagesWorkload) IsFailed(ctx *context.Context) (bool, error) { - return areAnyDataResourcesFailed(ctx, pyw.GetResourceIDs()) -} diff --git a/pkg/operator/workloads/workflow.go b/pkg/operator/workloads/workflow.go index d3d3981f7b..66211fe1dd 100644 --- a/pkg/operator/workloads/workflow.go +++ b/pkg/operator/workloads/workflow.go @@ -49,7 +49,6 @@ func PopulateWorkloadIDs(ctx *context.Context) error { return err } - populatePythonPackageWorkloadIDs(ctx, latestResourceWorkloadIDs) populateAPIWorkloadIDs(ctx, latestResourceWorkloadIDs) if err := ctx.CheckAllWorkloadIDsPopulated(); err != nil { @@ -60,7 +59,6 @@ func PopulateWorkloadIDs(ctx *context.Context) error { func extractWorkloads(ctx *context.Context) []Workload { var workloads []Workload - workloads = append(workloads, extractPythonPackageWorkloads(ctx)...) workloads = append(workloads, extractAPIWorkloads(ctx)...) workloads = append(workloads, extractHPAWorkloads(ctx)...) return workloads diff --git a/pkg/operator/workloads/workload.go b/pkg/operator/workloads/workload.go index 8afc2827c0..f0398ecda2 100644 --- a/pkg/operator/workloads/workload.go +++ b/pkg/operator/workloads/workload.go @@ -22,9 +22,8 @@ import ( ) const ( - workloadTypeAPI = "api" - workloadTypeHPA = "hpa" - workloadTypePythonPackager = "python-packager" + workloadTypeAPI = "api" + workloadTypeHPA = "hpa" ) type Workload interface { diff --git a/pkg/workloads/cortex/downloader/download.py b/pkg/workloads/cortex/downloader/download.py index 5bb843cb21..5be0e4501a 100644 --- a/pkg/workloads/cortex/downloader/download.py +++ b/pkg/workloads/cortex/downloader/download.py @@ -13,7 +13,11 @@ # limitations under the License. import argparse +import os +import base64 +import json +from cortex.lib import util from cortex.lib.storage import S3 from cortex.lib.log import get_logger @@ -21,16 +25,27 @@ def start(args): - bucket_name, prefix = S3.deconstruct_s3_path(args.download_from) - s3_client = S3(bucket_name, client_config={}) - s3_client.download(prefix, args.download_to) + download = json.loads(base64.urlsafe_b64decode(args.download)) + for download_arg in download: + from_path = download_arg["from"] + to_path = download_arg["to"] + bucket_name, prefix = S3.deconstruct_s3_path(from_path) + s3_client = S3(bucket_name, client_config={}) + s3_client.download(prefix, to_path) + if download_arg["unzip"]: + util.extract_zip( + os.path.join(to_path, os.path.basename(from_path)), delete_zip_file=True + ) def main(): parser = argparse.ArgumentParser() na = parser.add_argument_group("required named arguments") - na.add_argument("--download_from", required=True, help="Storage Path to download the file from") - na.add_argument("--download_to", required=True, help="Directory to download the file to") + na.add_argument( + "--download", + required=True, + help="a base64 encoded json array of download arg objects (see api_workloads.go for the structure)", + ) parser.set_defaults(func=start) args = parser.parse_args() diff --git a/pkg/workloads/cortex/lib/context.py b/pkg/workloads/cortex/lib/context.py index f646ebc7f7..42ea91747b 100644 --- a/pkg/workloads/cortex/lib/context.py +++ b/pkg/workloads/cortex/lib/context.py @@ -62,10 +62,11 @@ def __init__(self, **kwargs): self.root = self.ctx["root"] self.status_prefix = self.ctx["status_prefix"] self.app = self.ctx["app"] - self.python_packages = self.ctx["python_packages"] or {} self.apis = self.ctx["apis"] or {} self.api_version = self.cortex_config["api_version"] self.monitoring = None + self.project_id = self.ctx["project_id"] + self.project_key = self.ctx["project_key"] if "local_storage_path" in kwargs: self.storage = LocalStorage(base_dir=kwargs["local_storage_path"]) @@ -88,9 +89,8 @@ def __init__(self, **kwargs): os.environ["AWS_REGION"] = self.cortex_config.get("region", "") # ID maps - self.pp_id_map = ResourceMap(self.python_packages) if self.python_packages else None self.apis_id_map = ResourceMap(self.apis) if self.apis else None - self.id_map = util.merge_dicts_overwrite(self.pp_id_map, self.apis_id_map) + self.id_map = self.apis_id_map def download_file(self, impl_key, cache_impl_path): if not os.path.isfile(cache_impl_path): @@ -102,30 +102,20 @@ def download_python_file(self, impl_key, module_name): self.download_file(impl_key, cache_impl_path) return cache_impl_path - def load_module(self, module_prefix, module_name, impl_key): + def load_module(self, module_prefix, module_name, impl_path): full_module_name = "{}_{}".format(module_prefix, module_name) - - try: - impl_path = self.download_python_file(impl_key, full_module_name) - except CortexException as e: - e.wrap("unable to find python file") - raise - try: impl = imp.load_source(full_module_name, impl_path) except Exception as e: raise UserException("unable to load python file", str(e)) from e - return impl, impl_path + return impl - def get_request_handler_impl(self, api_name): + def get_request_handler_impl(self, api_name, project_dir): api = self.apis[api_name] - - module_prefix = "request_handler" - try: - impl, impl_path = self.load_module( - module_prefix, api["name"], api["request_handler_impl_key"] + impl = self.load_module( + "request_handler", api["name"], os.path.join(project_dir, api["request_handler"]) ) except CortexException as e: e.wrap("api " + api_name, "request_handler " + api["request_handler"]) diff --git a/pkg/workloads/cortex/lib/package.py b/pkg/workloads/cortex/lib/package.py deleted file mode 100644 index eac17c03cf..0000000000 --- a/pkg/workloads/cortex/lib/package.py +++ /dev/null @@ -1,199 +0,0 @@ -# Copyright 2019 Cortex Labs, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import argparse -import glob -from subprocess import run - -import requirements -from packaging.requirements import Requirement - -from cortex.lib import util, Context -from cortex.lib.log import get_logger -from cortex.lib.exceptions import UserException, CortexException - -logger = get_logger() - -LOCAL_PACKAGE_PATH = "/packages" -WHEELHOUSE_PATH = "/wheelhouse" - - -def get_build_order(python_packages): - build_order = [] - if "requirements.txt" in python_packages: - build_order.append("requirements.txt") - return build_order + sorted([name for name in python_packages if name != "requirements.txt"]) - - -def get_restricted_packages(): - req_list = ["tensorflow==1.14.0"] - req_files = glob.glob("/src/**/requirements.txt", recursive=True) - - for req_file in req_files: - # clean requirements file, like removing comments - with open(req_file) as f: - for req in requirements.parse(f): - specifiers = [op + version for op, version in req.specs] - req_list.append(req.name + ",".join(specifiers)) - - cortex_packages = {} - - for req_line in req_list: - parsed_req = Requirement(req_line) - if cortex_packages.get(parsed_req.name) is None: - cortex_packages[parsed_req.name] = parsed_req.specifier - else: - cortex_packages[parsed_req.name] = ( - cortex_packages[parsed_req.name] & parsed_req.specifier - ) - - return cortex_packages - - -def build_packages(python_packages, storage): - cmd_partial = {} - build_order = get_build_order(python_packages) - for package_name in build_order: - python_package = python_packages[package_name] - if package_name == "requirements.txt": - requirements_path = os.path.join(LOCAL_PACKAGE_PATH, package_name) - storage.download_file(python_package["src_key"], requirements_path) - cmd_partial[package_name] = "-r " + requirements_path - else: - storage.download_and_unzip(python_package["src_key"], LOCAL_PACKAGE_PATH) - cmd_partial[package_name] = os.path.join(LOCAL_PACKAGE_PATH, package_name) - - logger.info("Setting up packages") - - restricted_packages = get_restricted_packages() - - for package_name in build_order: - package_wheel_path = os.path.join(WHEELHOUSE_PATH, package_name) - requirement = cmd_partial[package_name] - logger.info("Building: {}".format(package_name)) - completed_process = run( - "pip3 wheel -w {} {}".format(package_wheel_path, requirement).split() - ) - - if completed_process.returncode != 0: - raise UserException("creating wheels", package_name) - - for wheelname in os.listdir(package_wheel_path): - name_split = wheelname.split("-") - dist_name, version = name_split[0], name_split[1] - expected_version_specs = restricted_packages.get(dist_name, None) - if expected_version_specs is not None and not expected_version_specs.contains(version): - raise UserException( - "when installing {}, found {}=={} which conflicts with cortex's requirements {}{}".format( - package_name, dist_name, version, dist_name, expected_version_specs - ) - ) - - logger.info("Validating packages") - - for package_name in build_order: - requirement = cmd_partial[package_name] - logger.info("Installing: {}".format(package_name)) - completed_process = run( - "pip3 install --no-index --find-links={} {}".format( - os.path.join(WHEELHOUSE_PATH, package_name), requirement - ).split() - ) - if completed_process.returncode != 0: - raise UserException("installing package", package_name) - - logger.info("Caching built packages") - - for package_name in build_order: - storage.zip_and_upload( - os.path.join(WHEELHOUSE_PATH, package_name), - python_packages[package_name]["package_key"], - ) - - -def build(args): - ctx = Context(s3_path=args.context, cache_dir=args.cache_dir, workload_id=args.workload_id) - python_packages_list = [ctx.pp_id_map[id] for id in args.python_packages.split(",")] - python_packages = { - python_package["name"]: python_package for python_package in python_packages_list - } - ctx.upload_resource_status_start(*python_packages_list) - try: - build_packages(python_packages, ctx.storage) - timestamp = util.now_timestamp_rfc_3339() - logger.info("workload: {}, completed: {}".format(ctx.workload_id, timestamp)) - except CortexException as e: - e.wrap("error") - logger.exception(e) - ctx.upload_resource_status_failed(*python_packages_list) - except Exception as e: - logger.exception(e) - ctx.upload_resource_status_failed(*python_packages_list) - else: - ctx.upload_resource_status_success(*python_packages_list) - - -def install_packages(python_packages, storage): - build_order = get_build_order(python_packages) - - for package_name in build_order: - python_package = python_packages[package_name] - storage.download_and_unzip( - python_package["package_key"], os.path.join(WHEELHOUSE_PATH, package_name) - ) - - if "requirements.txt" in python_packages: - storage.download_file(python_packages["requirements.txt"]["src_key"], "/requirements.txt") - - for package_name in build_order: - cmd = package_name - if package_name == "requirements.txt": - cmd = "-r /requirements.txt" - - completed_process = run( - "pip3 install --no-cache-dir --no-index --find-links={} {}".format( - os.path.join(WHEELHOUSE_PATH, package_name), cmd - ).split() - ) - if completed_process.returncode != 0: - raise UserException("installing package", package_name) - - util.rm_file("/requirements.txt") - util.rm_dir(WHEELHOUSE_PATH) - - -def main(): - parser = argparse.ArgumentParser() - na = parser.add_argument_group("required named arguments") - na.add_argument("--workload-id", required=True, help="Workload ID") - na.add_argument( - "--context", required=True, help="S3 path to context (e.g. s3://bucket/path/to/context.json" - ) - na.add_argument("--cache-dir", required=True, help="Local path for the context cache") - na.add_argument("--python-packages", help="Resource ids of packages to build") - na.add_argument( - "--build", action="store_true", help="Flag to determine mode (build vs install)" - ) - - args, _ = parser.parse_known_args() - if args.build: - build(args) - else: - ctx = Context(s3_path=args.context, cache_dir=args.cache_dir, workload_id=args.workload_id) - install_packages(ctx.python_packages, ctx.storage) - - -if __name__ == "__main__": - main() diff --git a/pkg/workloads/cortex/lib/storage/s3.py b/pkg/workloads/cortex/lib/storage/s3.py index 83d81d7d4d..28c1e9464b 100644 --- a/pkg/workloads/cortex/lib/storage/s3.py +++ b/pkg/workloads/cortex/lib/storage/s3.py @@ -29,23 +29,12 @@ def __init__(self, bucket=None, region=None, client_config={}): self.bucket = bucket self.region = region - default_config = { - "use_ssl": True, - "verify": True, - "region_name": None, - "aws_access_key_id": None, - "aws_secret_access_key": None, - "aws_session_token": None, - } - if client_config is None: client_config = {} if region is not None: client_config["region_name"] = region - merged_client_config = util.merge_dicts_in_place_no_overwrite(client_config, default_config) - self.s3 = boto3.client("s3", **client_config) @staticmethod diff --git a/pkg/workloads/cortex/onnx_serve/api.py b/pkg/workloads/cortex/onnx_serve/api.py index 9bc0377e6a..6b3be7c9ce 100644 --- a/pkg/workloads/cortex/onnx_serve/api.py +++ b/pkg/workloads/cortex/onnx_serve/api.py @@ -23,7 +23,7 @@ import onnxruntime as rt import numpy as np -from cortex.lib import util, package, Context, api_utils +from cortex.lib import util, Context, api_utils from cortex.lib.storage import S3 from cortex.lib.log import get_logger, debug_obj from cortex.lib.exceptions import CortexException, UserRuntimeException, UserException @@ -234,6 +234,7 @@ def exceptions(e): def start(args): + api = None try: ctx = Context(s3_path=args.context, cache_dir=args.cache_dir, workload_id=args.workload_id) @@ -244,8 +245,9 @@ def start(args): _, prefix = ctx.storage.deconstruct_s3_path(api["model"]) model_path = os.path.join(args.model_dir, os.path.basename(prefix)) if api.get("request_handler") is not None: - package.install_packages(ctx.python_packages, ctx.storage) - local_cache["request_handler"] = ctx.get_request_handler_impl(api["name"]) + local_cache["request_handler"] = ctx.get_request_handler_impl( + api["name"], args.project_dir + ) sess = rt.InferenceSession(model_path) local_cache["sess"] = sess @@ -286,6 +288,7 @@ def main(): na.add_argument("--api", required=True, help="Resource id of api to serve") na.add_argument("--model-dir", required=True, help="Directory to download the model to") na.add_argument("--cache-dir", required=True, help="Local path for the context cache") + na.add_argument("--project-dir", required=True, help="Local path for the project zip file") parser.set_defaults(func=start) diff --git a/pkg/workloads/cortex/onnx_serve/run.sh b/pkg/workloads/cortex/onnx_serve/run.sh new file mode 100755 index 0000000000..729c676a76 --- /dev/null +++ b/pkg/workloads/cortex/onnx_serve/run.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# Copyright 2019 Cortex Labs, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +if [ -f "/mnt/project/requirements.txt" ]; then + pip3 install -r /mnt/project/requirements.txt +fi +/usr/bin/python3 /src/cortex/onnx_serve/api.py "$@" diff --git a/pkg/workloads/cortex/tf_api/api.py b/pkg/workloads/cortex/tf_api/api.py index ac392fc819..d2cbb9acba 100644 --- a/pkg/workloads/cortex/tf_api/api.py +++ b/pkg/workloads/cortex/tf_api/api.py @@ -28,7 +28,7 @@ from tensorflow_serving.apis import prediction_service_pb2_grpc from google.protobuf import json_format -from cortex.lib import util, package, Context, api_utils +from cortex.lib import util, Context, api_utils from cortex.lib.storage import S3 from cortex.lib.log import get_logger, debug_obj from cortex.lib.exceptions import UserRuntimeException, UserException @@ -320,8 +320,9 @@ def start(args): local_cache["ctx"] = ctx if api.get("request_handler") is not None: - package.install_packages(ctx.python_packages, ctx.storage) - local_cache["request_handler"] = ctx.get_request_handler_impl(api["name"]) + local_cache["request_handler"] = ctx.get_request_handler_impl( + api["name"], args.project_dir + ) except Exception as e: logger.exception("failed to start api") sys.exit(1) @@ -384,6 +385,7 @@ def main(): na.add_argument("--api", required=True, help="Resource id of api to serve") na.add_argument("--model-dir", required=True, help="Directory to download the model to") na.add_argument("--cache-dir", required=True, help="Local path for the context cache") + na.add_argument("--project-dir", required=True, help="Local path for the project zip file") parser.set_defaults(func=start) args = parser.parse_args() diff --git a/pkg/workloads/cortex/tf_api/run.sh b/pkg/workloads/cortex/tf_api/run.sh new file mode 100755 index 0000000000..802be7913a --- /dev/null +++ b/pkg/workloads/cortex/tf_api/run.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# Copyright 2019 Cortex Labs, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +if [ -f "/mnt/project/requirements.txt" ]; then + pip3 install -r /mnt/project/requirements.txt +fi +/usr/bin/python3 /src/cortex/tf_api/api.py "$@"