From 39209ac5e6641e26115ca96bd05fa22228b49e94 Mon Sep 17 00:00:00 2001 From: Ivan Zhang Date: Wed, 28 Aug 2019 15:07:10 -0400 Subject: [PATCH 01/22] progress --- cli/cmd/deploy.go | 39 +++++++++++-- cli/cmd/lib_client.go | 15 ----- pkg/consts/consts.go | 1 + pkg/lib/files/files.go | 7 +++ pkg/operator/api/context/context.go | 2 + pkg/operator/context/apis.go | 4 -- pkg/operator/context/context.go | 8 ++- pkg/operator/endpoints/deploy.go | 86 +++++++++++++++-------------- 8 files changed, 95 insertions(+), 67 deletions(-) diff --git a/cli/cmd/deploy.go b/cli/cmd/deploy.go index 6a98d601ce..5b545e82e7 100644 --- a/cli/cmd/deploy.go +++ b/cli/cmd/deploy.go @@ -23,6 +23,7 @@ import ( "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" @@ -53,21 +54,49 @@ func deploy(force bool, ignoreCache bool) { errors.Exit(err) } - zipInput := &zip.Input{ + params := map[string]string{ + "force": s.Bool(force), + "ignoreCache": s.Bool(ignoreCache), + } + + zipBytes, err := zip.ToMem(&zip.Input{ FileLists: []zip.FileListInput{ { Sources: allConfigPaths(root), RemovePrefix: root, }, }, + }) + + if err != nil { + errors.Exit(errors.Wrap(err, "failed to zip configuration file")) } - params := map[string]string{ - "force": s.Bool(force), - "ignoreCache": s.Bool(ignoreCache), + projectPaths, err := files.ListDirRecursive(root, false, files.IgnoreCortexYAML, files.IgnoreHiddenFiles) + if err != nil { + errors.Exit(err) + } + projectZipBytes, err := zip.ToMem(&zip.Input{ + FileLists: []zip.FileListInput{ + { + Sources: projectPaths, + RemovePrefix: root, + }, + }, + }) + + if err != nil { + errors.Exit(errors.Wrap(err, "failed to zip configuration file")) + } + + uploadInput := &HTTPUploadInput{ + Bytes: map[string][]byte{ + "config.zip": zipBytes, + "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/lib_client.go b/cli/cmd/lib_client.go index ba7535c1b5..3203ce5f71 100644 --- a/cli/cmd/lib_client.go +++ b/cli/cmd/lib_client.go @@ -38,7 +38,6 @@ import ( "github.com/cortexlabs/cortex/pkg/lib/files" "github.com/cortexlabs/cortex/pkg/lib/json" libtime "github.com/cortexlabs/cortex/pkg/lib/time" - "github.com/cortexlabs/cortex/pkg/lib/zip" "github.com/cortexlabs/cortex/pkg/operator/api/schema" ) @@ -139,20 +138,6 @@ func addFileToMultipart(fileName string, writer *multipart.Writer, reader io.Rea return nil } -func HTTPUploadZip(endpoint string, zipInput *zip.Input, fileName string, qParams ...map[string]string) ([]byte, error) { - zipBytes, err := zip.ToMem(zipInput) - if err != nil { - return nil, errors.Wrap(err, "failed to zip configuration file") - } - - uploadInput := &HTTPUploadInput{ - Bytes: map[string][]byte{ - fileName: zipBytes, - }, - } - return HTTPUpload(endpoint, uploadInput, qParams...) -} - func StreamLogs(appName string, resourceName string, resourceType string, verbose bool) error { interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt) diff --git a/pkg/consts/consts.go b/pkg/consts/consts.go index 6bb9c1875d..f56cc975bb 100644 --- a/pkg/consts/consts.go +++ b/pkg/consts/consts.go @@ -34,6 +34,7 @@ var ( APIsDir = "apis" PythonPackagesDir = "python_packages" RequestHandlersDir = "request_handlers" + ProjectDir = "project" ContextsDir = "contexts" ResourceStatusesDir = "resource_statuses" WorkloadSpecsDir = "workload_specs" diff --git a/pkg/lib/files/files.go b/pkg/lib/files/files.go index dea4de0b0e..072cd91c11 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 strings.HasSuffix(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/context.go b/pkg/operator/api/context/context.go index 5bc767fd70..0c92323f38 100644 --- a/pkg/operator/api/context/context.go +++ b/pkg/operator/api/context/context.go @@ -38,6 +38,8 @@ type Context struct { 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 { diff --git a/pkg/operator/context/apis.go b/pkg/operator/context/apis.go index 16fea5cfe5..c369fe2c47 100644 --- a/pkg/operator/context/apis.go +++ b/pkg/operator/context/apis.go @@ -25,7 +25,6 @@ import ( "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" @@ -33,8 +32,6 @@ import ( "github.com/cortexlabs/cortex/pkg/operator/config" ) -var uploadedRequestHandlers = strset.New() - func getAPIs(config *userconfig.Config, deploymentVersion string, impls map[string][]byte, @@ -102,6 +99,5 @@ func uploadRequestHandler(implKey string, impl []byte) error { } } - uploadedRequestHandlers.Add(implKey) return nil } diff --git a/pkg/operator/context/context.go b/pkg/operator/context/context.go index f005a37d2d..240dfa4e39 100644 --- a/pkg/operator/context/context.go +++ b/pkg/operator/context/context.go @@ -32,6 +32,7 @@ import ( func New( userconf *userconfig.Config, files map[string][]byte, + projectBytes []byte, ignoreCache bool, ) (*context.Context, error) { @@ -61,7 +62,6 @@ func New( ) ctx.StatusPrefix = StatusPrefix(ctx.App.Name) - pythonPackages, err := loadPythonPackages(files, ctx.DeploymentVersion) if err != nil { return nil, err @@ -80,6 +80,11 @@ func New( return nil, err } + ctx.ProjectID = hash.Bytes(projectBytes) + ctx.ProjectKey = filepath.Join(consts.ProjectDir, ctx.ProjectID) + if err = config.AWS.UploadBytesToS3(projectBytes, ctx.ProjectKey); err != nil { + return nil, err + } ctx.ID = calculateID(ctx) ctx.Key = ctxKey(ctx.ID, ctx.App.Name) return ctx, nil @@ -101,6 +106,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()) diff --git a/pkg/operator/endpoints/deploy.go b/pkg/operator/endpoints/deploy.go index 74a7d7987e..19c6871952 100644 --- a/pkg/operator/endpoints/deploy.go +++ b/pkg/operator/endpoints/deploy.go @@ -37,7 +37,41 @@ func Deploy(w http.ResponseWriter, r *http.Request) { ignoreCache := getOptionalBoolQParam("ignoreCache", false, r) force := getOptionalBoolQParam("force", false, r) - ctx, err := getContext(r, ignoreCache) + zipBytes, err := files.ReadReqFile(r, "config.zip") + if err != nil { + RespondError(w, errors.WithStack(err)) + return + } + + if len(zipBytes) == 0 { + RespondError(w, ErrorFormFileMustBeProvided("config.zip")) + return + } + + zipContents, err := zip.UnzipMemToMem(zipBytes) + if err != nil { + RespondError(w, errors.Wrap(err, "form file", "config.zip")) + return + } + + userconf, err := userconfig.New(zipContents) + if err != nil { + RespondError(w, err) + return + } + + projectBytes, err := files.ReadReqFile(r, "project.zip") + if err != nil { + RespondError(w, errors.WithStack(err)) + return + } + + ctx, err := ocontext.New(userconf, zipContents, projectBytes, ignoreCache) + if err != nil { + RespondError(w, err) + return + } + if err != nil { RespondError(w, err) return @@ -66,11 +100,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 } } @@ -89,50 +123,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") - } - - zipContents, err := zip.UnzipMemToMem(zipBytes) - if err != nil { - return nil, errors.Wrap(err, "form file", "config.zip") - } - - config, err := userconfig.New(zipContents) - if err != nil { - return nil, err - } - - ctx, err := ocontext.New(config, zipContents, ignoreCache) - if err != nil { - return nil, err + Respond(w, schema.DeployResponse{Message: ResDeploymentUpdated}) } - - return ctx, nil } From 72603a9b5d5c7a61157c6f09566d8b187d067e34 Mon Sep 17 00:00:00 2001 From: Ivan Zhang Date: Wed, 28 Aug 2019 17:55:56 -0400 Subject: [PATCH 02/22] progress --- pkg/operator/workloads/api_workload.go | 13 ++++++++++++- pkg/workloads/cortex/downloader/download.py | 1 + pkg/workloads/cortex/lib/storage/s3.py | 11 ----------- pkg/workloads/cortex/tf_api/api.py | 2 ++ 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/pkg/operator/workloads/api_workload.go b/pkg/operator/workloads/api_workload.go index 1ee0af9a6c..bf87906d67 100644 --- a/pkg/operator/workloads/api_workload.go +++ b/pkg/operator/workloads/api_workload.go @@ -281,7 +281,7 @@ func tfAPISpec( RestartPolicy: "Always", InitContainers: []kcore.Container{ { - Name: downloaderInitContainerName, + Name: downloaderInitContainerName + "-model", Image: config.Cortex.DownloaderImage, ImagePullPolicy: "Always", Args: []string{ @@ -291,6 +291,17 @@ func tfAPISpec( Env: k8s.AWSCredentials(), VolumeMounts: k8s.DefaultVolumeMounts(), }, + { + Name: downloaderInitContainerName + "-project", + Image: config.Cortex.DownloaderImage, + ImagePullPolicy: "Always", + Args: []string{ + "--download_from=" + config.AWS.S3Path(ctx.ProjectKey), + "--download_to=" + path.Join(consts.EmptyDirMountPath, "project", "project.zip"), + }, + Env: k8s.AWSCredentials(), + VolumeMounts: k8s.DefaultVolumeMounts(), + }, }, Containers: []kcore.Container{ { diff --git a/pkg/workloads/cortex/downloader/download.py b/pkg/workloads/cortex/downloader/download.py index 5bb843cb21..46c85d660f 100644 --- a/pkg/workloads/cortex/downloader/download.py +++ b/pkg/workloads/cortex/downloader/download.py @@ -23,6 +23,7 @@ def start(args): bucket_name, prefix = S3.deconstruct_s3_path(args.download_from) s3_client = S3(bucket_name, client_config={}) + logger.info(args.download_to) s3_client.download(prefix, args.download_to) 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/tf_api/api.py b/pkg/workloads/cortex/tf_api/api.py index 3caaf2889c..b1c98056c7 100644 --- a/pkg/workloads/cortex/tf_api/api.py +++ b/pkg/workloads/cortex/tf_api/api.py @@ -341,6 +341,7 @@ def exceptions(e): def start(args): + util.extract_zip(args.project_dir+"project.zip", delete_zip_file=True) api = None try: ctx = Context(s3_path=args.context, cache_dir=args.cache_dir, workload_id=args.workload_id) @@ -415,6 +416,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() From 9716b8fa968b07dabcee4c202f9b0f5761964c3a Mon Sep 17 00:00:00 2001 From: Ivan Zhang Date: Wed, 28 Aug 2019 18:59:06 -0400 Subject: [PATCH 03/22] python packager --- examples/text-generator/cortex.yaml | 2 +- examples/text-generator/encoder.py | 13 ------------- examples/text-generator/handler.py | 12 ++++++++++++ images/onnx-serve-gpu/Dockerfile | 2 +- images/onnx-serve/Dockerfile | 2 +- images/tf-api/Dockerfile | 2 +- pkg/operator/context/context.go | 2 +- pkg/operator/workloads/api_workload.go | 4 +++- pkg/workloads/cortex/downloader/download.py | 1 - pkg/workloads/cortex/onnx_serve/api.py | 2 ++ pkg/workloads/cortex/tf_api/api.py | 2 +- 11 files changed, 23 insertions(+), 21 deletions(-) create mode 100644 examples/text-generator/handler.py 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..21e2303e87 --- /dev/null +++ b/examples/text-generator/handler.py @@ -0,0 +1,12 @@ +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..b05eef038e 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 diff --git a/images/onnx-serve/Dockerfile b/images/onnx-serve/Dockerfile index efb3b615c8..29addd04ab 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 diff --git a/images/tf-api/Dockerfile b/images/tf-api/Dockerfile index d3115fd473..1b7fb7f8f5 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 \ diff --git a/pkg/operator/context/context.go b/pkg/operator/context/context.go index 240dfa4e39..449f027eea 100644 --- a/pkg/operator/context/context.go +++ b/pkg/operator/context/context.go @@ -81,7 +81,7 @@ func New( } ctx.ProjectID = hash.Bytes(projectBytes) - ctx.ProjectKey = filepath.Join(consts.ProjectDir, ctx.ProjectID) + ctx.ProjectKey = filepath.Join(consts.ProjectDir, ctx.ProjectID, "project.zip") if err = config.AWS.UploadBytesToS3(projectBytes, ctx.ProjectKey); err != nil { return nil, err } diff --git a/pkg/operator/workloads/api_workload.go b/pkg/operator/workloads/api_workload.go index bf87906d67..b7fa3149f7 100644 --- a/pkg/operator/workloads/api_workload.go +++ b/pkg/operator/workloads/api_workload.go @@ -297,7 +297,7 @@ func tfAPISpec( ImagePullPolicy: "Always", Args: []string{ "--download_from=" + config.AWS.S3Path(ctx.ProjectKey), - "--download_to=" + path.Join(consts.EmptyDirMountPath, "project", "project.zip"), + "--download_to=" + path.Join(consts.EmptyDirMountPath, "project"), }, Env: k8s.AWSCredentials(), VolumeMounts: k8s.DefaultVolumeMounts(), @@ -316,6 +316,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(), @@ -460,6 +461,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/workloads/cortex/downloader/download.py b/pkg/workloads/cortex/downloader/download.py index 46c85d660f..5bb843cb21 100644 --- a/pkg/workloads/cortex/downloader/download.py +++ b/pkg/workloads/cortex/downloader/download.py @@ -23,7 +23,6 @@ def start(args): bucket_name, prefix = S3.deconstruct_s3_path(args.download_from) s3_client = S3(bucket_name, client_config={}) - logger.info(args.download_to) s3_client.download(prefix, args.download_to) diff --git a/pkg/workloads/cortex/onnx_serve/api.py b/pkg/workloads/cortex/onnx_serve/api.py index 2cf95c7afe..369385774f 100644 --- a/pkg/workloads/cortex/onnx_serve/api.py +++ b/pkg/workloads/cortex/onnx_serve/api.py @@ -253,6 +253,8 @@ def exceptions(e): def start(args): + util.extract_zip(os.path.join(args.project_dir, "project.zip"), delete_zip_file=True) + api = None try: ctx = Context(s3_path=args.context, cache_dir=args.cache_dir, workload_id=args.workload_id) diff --git a/pkg/workloads/cortex/tf_api/api.py b/pkg/workloads/cortex/tf_api/api.py index b1c98056c7..21b17e2a2b 100644 --- a/pkg/workloads/cortex/tf_api/api.py +++ b/pkg/workloads/cortex/tf_api/api.py @@ -341,7 +341,7 @@ def exceptions(e): def start(args): - util.extract_zip(args.project_dir+"project.zip", delete_zip_file=True) + util.extract_zip(os.path.join(args.project_dir, "project.zip"), delete_zip_file=True) api = None try: ctx = Context(s3_path=args.context, cache_dir=args.cache_dir, workload_id=args.workload_id) From 4b4b03f0795c0a65b6d18c4fccdba1aab1ca1262 Mon Sep 17 00:00:00 2001 From: Ivan Zhang Date: Thu, 29 Aug 2019 11:19:50 -0400 Subject: [PATCH 04/22] address comments --- cli/cmd/deploy.go | 23 +++++++++++------------ examples/text-generator/handler.py | 1 + pkg/consts/consts.go | 2 +- pkg/lib/files/files.go | 2 +- pkg/operator/context/context.go | 6 +++++- pkg/operator/endpoints/deploy.go | 21 +++++++-------------- pkg/workloads/cortex/onnx_serve/api.py | 1 + 7 files changed, 27 insertions(+), 29 deletions(-) diff --git a/cli/cmd/deploy.go b/cli/cmd/deploy.go index 5b545e82e7..f00fd69fcc 100644 --- a/cli/cmd/deploy.go +++ b/cli/cmd/deploy.go @@ -18,6 +18,7 @@ package cmd import ( "fmt" + "io/ioutil" "github.com/spf13/cobra" @@ -59,23 +60,21 @@ func deploy(force bool, ignoreCache bool) { "ignoreCache": s.Bool(ignoreCache), } - zipBytes, err := zip.ToMem(&zip.Input{ - FileLists: []zip.FileListInput{ - { - Sources: allConfigPaths(root), - RemovePrefix: root, - }, - }, - }) - + configBytes, err := ioutil.ReadFile("cortex.yaml") if err != nil { - errors.Exit(errors.Wrap(err, "failed to zip configuration file")) + errors.Exit(errors.Wrap(err, "failed to read configuration file")) } - projectPaths, err := files.ListDirRecursive(root, false, files.IgnoreCortexYAML, files.IgnoreHiddenFiles) + 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{ { @@ -91,7 +90,7 @@ func deploy(force bool, ignoreCache bool) { uploadInput := &HTTPUploadInput{ Bytes: map[string][]byte{ - "config.zip": zipBytes, + "config.yaml": configBytes, "project.zip": projectZipBytes, }, } diff --git a/examples/text-generator/handler.py b/examples/text-generator/handler.py index 21e2303e87..5be581ba64 100644 --- a/examples/text-generator/handler.py +++ b/examples/text-generator/handler.py @@ -1,6 +1,7 @@ from encoder import get_encoder encoder = get_encoder() + def pre_inference(sample, metadata): context = encoder.encode(sample["text"]) return {"context": [context]} diff --git a/pkg/consts/consts.go b/pkg/consts/consts.go index f56cc975bb..048d621f08 100644 --- a/pkg/consts/consts.go +++ b/pkg/consts/consts.go @@ -34,7 +34,7 @@ var ( APIsDir = "apis" PythonPackagesDir = "python_packages" RequestHandlersDir = "request_handlers" - ProjectDir = "project" + ProjectDir = "projects" ContextsDir = "contexts" ResourceStatusesDir = "resource_statuses" WorkloadSpecsDir = "workload_specs" diff --git a/pkg/lib/files/files.go b/pkg/lib/files/files.go index 072cd91c11..830987bb66 100644 --- a/pkg/lib/files/files.go +++ b/pkg/lib/files/files.go @@ -242,7 +242,7 @@ func IgnoreHiddenFiles(path string, fi os.FileInfo) (bool, error) { } func IgnoreCortexYAML(path string, fi os.FileInfo) (bool, error) { - if strings.HasSuffix(fi.Name(), "cortex.yaml") { + if !fi.IsDir() && fi.Name() == "cortex.yaml" { return true, nil } return false, nil diff --git a/pkg/operator/context/context.go b/pkg/operator/context/context.go index 449f027eea..3741acaf83 100644 --- a/pkg/operator/context/context.go +++ b/pkg/operator/context/context.go @@ -24,6 +24,7 @@ import ( "github.com/cortexlabs/cortex/pkg/consts" "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/userconfig" "github.com/cortexlabs/cortex/pkg/operator/config" @@ -31,10 +32,13 @@ import ( func New( userconf *userconfig.Config, - files map[string][]byte, projectBytes []byte, ignoreCache bool, ) (*context.Context, error) { + files, err := zip.UnzipMemToMem(projectBytes) + if err != nil { + return nil, err + } ctx := &context.Context{} ctx.CreatedEpoch = time.Now().Unix() diff --git a/pkg/operator/endpoints/deploy.go b/pkg/operator/endpoints/deploy.go index c1854b4168..0ec539cc28 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,36 +36,30 @@ func Deploy(w http.ResponseWriter, r *http.Request) { ignoreCache := getOptionalBoolQParam("ignoreCache", false, r) force := getOptionalBoolQParam("force", false, r) - zipBytes, err := files.ReadReqFile(r, "config.zip") + configBytes, err := files.ReadReqFile(r, "config.yaml") if err != nil { RespondError(w, errors.WithStack(err)) return } - if len(zipBytes) == 0 { - RespondError(w, ErrorFormFileMustBeProvided("config.zip")) + if len(configBytes) == 0 { + RespondError(w, ErrorFormFileMustBeProvided("config.yaml")) return } - zipContents, err := zip.UnzipMemToMem(zipBytes) + projectBytes, err := files.ReadReqFile(r, "project.zip") if err != nil { - RespondError(w, errors.Wrap(err, "form file", "config.zip")) + RespondError(w, errors.WithStack(err)) return } - userconf, err := userconfig.New("cortex.yaml", zipContents["cortex.yaml"], true) + 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 - } - - ctx, err := ocontext.New(userconf, zipContents, projectBytes, ignoreCache) + ctx, err := ocontext.New(userconf, projectBytes, ignoreCache) if err != nil { RespondError(w, err) return diff --git a/pkg/workloads/cortex/onnx_serve/api.py b/pkg/workloads/cortex/onnx_serve/api.py index 369385774f..ba464ad797 100644 --- a/pkg/workloads/cortex/onnx_serve/api.py +++ b/pkg/workloads/cortex/onnx_serve/api.py @@ -305,6 +305,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) From dab8dcff9258fdb68a0fef2a7281ae2c60706e5a Mon Sep 17 00:00:00 2001 From: Ivan Zhang Date: Thu, 29 Aug 2019 11:22:09 -0400 Subject: [PATCH 05/22] format --- examples/text-generator/handler.py | 2 +- pkg/workloads/cortex/onnx_serve/api.py | 2 +- pkg/workloads/cortex/tf_api/api.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/text-generator/handler.py b/examples/text-generator/handler.py index 5be581ba64..5e3321b330 100644 --- a/examples/text-generator/handler.py +++ b/examples/text-generator/handler.py @@ -2,6 +2,7 @@ encoder = get_encoder() + def pre_inference(sample, metadata): context = encoder.encode(sample["text"]) return {"context": [context]} @@ -10,4 +11,3 @@ def pre_inference(sample, metadata): def post_inference(prediction, metadata): response = prediction["response"]["sample"] return {encoder.decode(response)} - diff --git a/pkg/workloads/cortex/onnx_serve/api.py b/pkg/workloads/cortex/onnx_serve/api.py index ba464ad797..5c12e41abb 100644 --- a/pkg/workloads/cortex/onnx_serve/api.py +++ b/pkg/workloads/cortex/onnx_serve/api.py @@ -253,7 +253,7 @@ def exceptions(e): def start(args): - util.extract_zip(os.path.join(args.project_dir, "project.zip"), delete_zip_file=True) + util.extract_zip(os.path.join(args.project_dir, "project.zip"), delete_zip_file=True) api = None try: diff --git a/pkg/workloads/cortex/tf_api/api.py b/pkg/workloads/cortex/tf_api/api.py index 21b17e2a2b..d5c2df06ca 100644 --- a/pkg/workloads/cortex/tf_api/api.py +++ b/pkg/workloads/cortex/tf_api/api.py @@ -341,7 +341,7 @@ def exceptions(e): def start(args): - util.extract_zip(os.path.join(args.project_dir, "project.zip"), delete_zip_file=True) + util.extract_zip(os.path.join(args.project_dir, "project.zip"), delete_zip_file=True) api = None try: ctx = Context(s3_path=args.context, cache_dir=args.cache_dir, workload_id=args.workload_id) From 969950effdbe022c3b8677add9897393a6df36c9 Mon Sep 17 00:00:00 2001 From: Ivan Zhang Date: Thu, 29 Aug 2019 11:38:19 -0400 Subject: [PATCH 06/22] unexport --- pkg/operator/context/context.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/operator/context/context.go b/pkg/operator/context/context.go index 3741acaf83..8637986efa 100644 --- a/pkg/operator/context/context.go +++ b/pkg/operator/context/context.go @@ -65,7 +65,7 @@ func New( consts.MetadataDir, ) - ctx.StatusPrefix = StatusPrefix(ctx.App.Name) + ctx.StatusPrefix = statusPrefix(ctx.App.Name) pythonPackages, err := loadPythonPackages(files, ctx.DeploymentVersion) if err != nil { return nil, err @@ -131,7 +131,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, @@ -141,7 +141,7 @@ func StatusPrefix(appName string) string { func StatusKey(resourceID string, workloadID string, appName string) string { return filepath.Join( - StatusPrefix(appName), + statusPrefix(appName), resourceID, workloadID, ) @@ -149,7 +149,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", ) From 1ec9a06916b358bea30fc8d22d5e9b78d3c2b12a Mon Sep 17 00:00:00 2001 From: Ivan Zhang Date: Thu, 29 Aug 2019 17:42:28 -0400 Subject: [PATCH 07/22] remove python packages --- cli/cmd/get.go | 47 ----- cli/cmd/lib_config_reader.go | 23 -- docs/deployments/python-packages.md | 32 --- pkg/consts/consts.go | 4 +- pkg/operator/api/context/context.go | 13 -- pkg/operator/api/context/dependencies.go | 15 -- pkg/operator/api/context/python_packages.go | 35 ---- pkg/operator/api/resource/type.go | 1 - pkg/operator/context/apis.go | 5 - pkg/operator/context/context.go | 8 +- pkg/operator/context/python_packages.go | 125 ----------- .../workloads/python_package_workload.go | 132 ------------ pkg/operator/workloads/workflow.go | 2 - pkg/workloads/cortex/lib/context.py | 3 - pkg/workloads/cortex/lib/package.py | 198 ------------------ pkg/workloads/cortex/lib/packages.py | 13 ++ pkg/workloads/cortex/onnx_serve/api.py | 4 +- pkg/workloads/cortex/tf_api/api.py | 6 +- 18 files changed, 20 insertions(+), 646 deletions(-) delete mode 100644 pkg/operator/api/context/python_packages.go delete mode 100644 pkg/operator/context/python_packages.go delete mode 100644 pkg/operator/workloads/python_package_workload.go delete mode 100644 pkg/workloads/cortex/lib/package.py create mode 100644 pkg/workloads/cortex/lib/packages.py diff --git a/cli/cmd/get.go b/cli/cmd/get.go index 99c714f9d7..9e29a34f40 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 { @@ -611,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)) @@ -639,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/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/pkg/consts/consts.go b/pkg/consts/consts.go index 048d621f08..a6f943cad9 100644 --- a/pkg/consts/consts.go +++ b/pkg/consts/consts.go @@ -26,13 +26,11 @@ var ( CortexConfigPath = "/configs/cortex" CortexConfigName = "cortex-config" - RequirementsTxt = "requirements.txt" - PackageDir = "packages" + PackageDir = "packages" AppsDir = "apps" DeploymentsDir = "deployments" APIsDir = "apis" - PythonPackagesDir = "python_packages" RequestHandlersDir = "request_handlers" ProjectDir = "projects" ContextsDir = "contexts" diff --git a/pkg/operator/api/context/context.go b/pkg/operator/api/context/context.go index 0c92323f38..f073679f49 100644 --- a/pkg/operator/api/context/context.go +++ b/pkg/operator/api/context/context.go @@ -36,7 +36,6 @@ 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"` @@ -85,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 } @@ -172,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) } @@ -205,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..852fb47424 100644 --- a/pkg/operator/api/resource/type.go +++ b/pkg/operator/api/resource/type.go @@ -42,7 +42,6 @@ var ( "unknown", "deployments", "apis", - "python_packages", } userFacing = []string{ diff --git a/pkg/operator/context/apis.go b/pkg/operator/context/apis.go index c369fe2c47..ec29968260 100644 --- a/pkg/operator/context/apis.go +++ b/pkg/operator/context/apis.go @@ -35,7 +35,6 @@ import ( func getAPIs(config *userconfig.Config, deploymentVersion string, impls map[string][]byte, - pythonPackages context.PythonPackages, ) (context.APIs, error) { apis := context.APIs{} @@ -47,10 +46,6 @@ func getAPIs(config *userconfig.Config, 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) diff --git a/pkg/operator/context/context.go b/pkg/operator/context/context.go index 8637986efa..16c07ea2b6 100644 --- a/pkg/operator/context/context.go +++ b/pkg/operator/context/context.go @@ -66,13 +66,7 @@ func New( ) ctx.StatusPrefix = statusPrefix(ctx.App.Name) - pythonPackages, err := loadPythonPackages(files, ctx.DeploymentVersion) - if err != nil { - return nil, err - } - ctx.PythonPackages = pythonPackages - - apis, err := getAPIs(userconf, ctx.DeploymentVersion, files, pythonPackages) + apis, err := getAPIs(userconf, ctx.DeploymentVersion, files) if err != nil { return nil, err 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/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/workloads/cortex/lib/context.py b/pkg/workloads/cortex/lib/context.py index dda42f1c6f..9621a6a03f 100644 --- a/pkg/workloads/cortex/lib/context.py +++ b/pkg/workloads/cortex/lib/context.py @@ -62,7 +62,6 @@ 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 @@ -88,9 +87,7 @@ 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) def download_file(self, impl_key, cache_impl_path): if not os.path.isfile(cache_impl_path): diff --git a/pkg/workloads/cortex/lib/package.py b/pkg/workloads/cortex/lib/package.py deleted file mode 100644 index 5d3d1e6b94..0000000000 --- a/pkg/workloads/cortex/lib/package.py +++ /dev/null @@ -1,198 +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) - util.log_job_finished(ctx.workload_id) - 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/packages.py b/pkg/workloads/cortex/lib/packages.py new file mode 100644 index 0000000000..a6a56181ec --- /dev/null +++ b/pkg/workloads/cortex/lib/packages.py @@ -0,0 +1,13 @@ + +from subprocess import run + +from cortex.lib.exceptions import UserException, CortexException + +def install(project_path): + completed_process = run( + "pip3 install -r {}/requirements.txt".format( + project_path + ).split() + ) + if completed_process.returncode != 0: + raise UserException("installing packages") diff --git a/pkg/workloads/cortex/onnx_serve/api.py b/pkg/workloads/cortex/onnx_serve/api.py index 5c12e41abb..d8133c4f64 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, packages, Context, api_utils from cortex.lib.storage import S3 from cortex.lib.log import get_logger from cortex.lib.exceptions import CortexException, UserRuntimeException, UserException @@ -257,6 +257,7 @@ def start(args): api = None try: + packages.install(args.project_dir) ctx = Context(s3_path=args.context, cache_dir=args.cache_dir, workload_id=args.workload_id) api = ctx.apis_id_map[args.api] local_cache["api"] = api @@ -265,7 +266,6 @@ 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"]) sess = rt.InferenceSession(model_path) diff --git a/pkg/workloads/cortex/tf_api/api.py b/pkg/workloads/cortex/tf_api/api.py index d5c2df06ca..f941742b0f 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, packages, Context, api_utils from cortex.lib.storage import S3 from cortex.lib.log import get_logger from cortex.lib.exceptions import CortexException, UserRuntimeException, UserException @@ -341,16 +341,16 @@ def exceptions(e): def start(args): - util.extract_zip(os.path.join(args.project_dir, "project.zip"), delete_zip_file=True) + util.extract_zip(os.path.join(args.project_dir, "project.zip")) api = None try: + packages.install(args.project_dir) ctx = Context(s3_path=args.context, cache_dir=args.cache_dir, workload_id=args.workload_id) api = ctx.apis_id_map[args.api] local_cache["api"] = api 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"]) except CortexException as e: e.wrap("error") From e0c943aa0e223b3ff6fcf4a0d8db38fedfd92e26 Mon Sep 17 00:00:00 2001 From: Ivan Zhang Date: Thu, 29 Aug 2019 17:45:01 -0400 Subject: [PATCH 08/22] format --- pkg/workloads/cortex/lib/packages.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/pkg/workloads/cortex/lib/packages.py b/pkg/workloads/cortex/lib/packages.py index a6a56181ec..63923461ff 100644 --- a/pkg/workloads/cortex/lib/packages.py +++ b/pkg/workloads/cortex/lib/packages.py @@ -1,13 +1,9 @@ - from subprocess import run from cortex.lib.exceptions import UserException, CortexException + def install(project_path): - completed_process = run( - "pip3 install -r {}/requirements.txt".format( - project_path - ).split() - ) + completed_process = run("pip3 install -r {}/requirements.txt".format(project_path).split()) if completed_process.returncode != 0: raise UserException("installing packages") From a3e3f4e222505768095348e34c5bfa59c5f2feb4 Mon Sep 17 00:00:00 2001 From: Ivan Zhang Date: Thu, 29 Aug 2019 17:45:16 -0400 Subject: [PATCH 09/22] format license --- pkg/workloads/cortex/lib/packages.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pkg/workloads/cortex/lib/packages.py b/pkg/workloads/cortex/lib/packages.py index 63923461ff..b7a4687af1 100644 --- a/pkg/workloads/cortex/lib/packages.py +++ b/pkg/workloads/cortex/lib/packages.py @@ -1,3 +1,18 @@ +# 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. + + from subprocess import run from cortex.lib.exceptions import UserException, CortexException From b7c0a25e119193806b6bbcb737c4d769380b033f Mon Sep 17 00:00:00 2001 From: Ivan Zhang Date: Thu, 29 Aug 2019 18:11:43 -0400 Subject: [PATCH 10/22] address comments --- cli/cmd/deploy.go | 7 ++++--- pkg/consts/consts.go | 2 +- pkg/operator/context/context.go | 2 +- pkg/operator/endpoints/deploy.go | 4 ++-- pkg/operator/workloads/api_workload.go | 13 ++++++++++++- pkg/workloads/cortex/lib/context.py | 2 ++ pkg/workloads/cortex/onnx_serve/api.py | 5 +++-- pkg/workloads/cortex/tf_api/api.py | 5 +++-- 8 files changed, 28 insertions(+), 12 deletions(-) diff --git a/cli/cmd/deploy.go b/cli/cmd/deploy.go index f00fd69fcc..33148bfd83 100644 --- a/cli/cmd/deploy.go +++ b/cli/cmd/deploy.go @@ -29,6 +29,7 @@ import ( 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 @@ -62,7 +63,7 @@ func deploy(force bool, ignoreCache bool) { configBytes, err := ioutil.ReadFile("cortex.yaml") if err != nil { - errors.Exit(errors.Wrap(err, "failed to read configuration file")) + errors.Exit(errors.Wrap(err, userconfig.ErrorReadConfig().Error())) } projectPaths, err := files.ListDirRecursive(root, false, @@ -85,12 +86,12 @@ func deploy(force bool, ignoreCache bool) { }) if err != nil { - errors.Exit(errors.Wrap(err, "failed to zip configuration file")) + errors.Exit(errors.Wrap(err, "failed to zip project folder")) } uploadInput := &HTTPUploadInput{ Bytes: map[string][]byte{ - "config.yaml": configBytes, + "cortex.yaml": configBytes, "project.zip": projectZipBytes, }, } diff --git a/pkg/consts/consts.go b/pkg/consts/consts.go index a6f943cad9..acc971bba8 100644 --- a/pkg/consts/consts.go +++ b/pkg/consts/consts.go @@ -32,7 +32,7 @@ var ( DeploymentsDir = "deployments" APIsDir = "apis" RequestHandlersDir = "request_handlers" - ProjectDir = "projects" + ProjectsDir = "projects" ContextsDir = "contexts" ResourceStatusesDir = "resource_statuses" WorkloadSpecsDir = "workload_specs" diff --git a/pkg/operator/context/context.go b/pkg/operator/context/context.go index 16c07ea2b6..ec4f2b39fb 100644 --- a/pkg/operator/context/context.go +++ b/pkg/operator/context/context.go @@ -79,7 +79,7 @@ func New( } ctx.ProjectID = hash.Bytes(projectBytes) - ctx.ProjectKey = filepath.Join(consts.ProjectDir, ctx.ProjectID, "project.zip") + ctx.ProjectKey = filepath.Join(consts.ProjectsDir, ctx.ProjectID+".zip") if err = config.AWS.UploadBytesToS3(projectBytes, ctx.ProjectKey); err != nil { return nil, err } diff --git a/pkg/operator/endpoints/deploy.go b/pkg/operator/endpoints/deploy.go index 0ec539cc28..8b65bc5e70 100644 --- a/pkg/operator/endpoints/deploy.go +++ b/pkg/operator/endpoints/deploy.go @@ -36,14 +36,14 @@ func Deploy(w http.ResponseWriter, r *http.Request) { ignoreCache := getOptionalBoolQParam("ignoreCache", false, r) force := getOptionalBoolQParam("force", false, r) - configBytes, err := files.ReadReqFile(r, "config.yaml") + configBytes, err := files.ReadReqFile(r, "cortex.yaml") if err != nil { RespondError(w, errors.WithStack(err)) return } if len(configBytes) == 0 { - RespondError(w, ErrorFormFileMustBeProvided("config.yaml")) + RespondError(w, ErrorFormFileMustBeProvided("cortex.yaml")) return } diff --git a/pkg/operator/workloads/api_workload.go b/pkg/operator/workloads/api_workload.go index b7fa3149f7..3f6970de0e 100644 --- a/pkg/operator/workloads/api_workload.go +++ b/pkg/operator/workloads/api_workload.go @@ -438,7 +438,7 @@ func onnxAPISpec( K8sPodSpec: kcore.PodSpec{ InitContainers: []kcore.Container{ { - Name: downloaderInitContainerName, + Name: downloaderInitContainerName+"-model", Image: config.Cortex.DownloaderImage, ImagePullPolicy: "Always", Args: []string{ @@ -448,6 +448,17 @@ func onnxAPISpec( Env: k8s.AWSCredentials(), VolumeMounts: k8s.DefaultVolumeMounts(), }, + { + Name: downloaderInitContainerName + "-project", + Image: config.Cortex.DownloaderImage, + ImagePullPolicy: "Always", + Args: []string{ + "--download_from=" + config.AWS.S3Path(ctx.ProjectKey), + "--download_to=" + path.Join(consts.EmptyDirMountPath, "project"), + }, + Env: k8s.AWSCredentials(), + VolumeMounts: k8s.DefaultVolumeMounts(), + }, }, Containers: []kcore.Container{ { diff --git a/pkg/workloads/cortex/lib/context.py b/pkg/workloads/cortex/lib/context.py index 9621a6a03f..0de04a4b35 100644 --- a/pkg/workloads/cortex/lib/context.py +++ b/pkg/workloads/cortex/lib/context.py @@ -65,6 +65,8 @@ def __init__(self, **kwargs): 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"]) diff --git a/pkg/workloads/cortex/onnx_serve/api.py b/pkg/workloads/cortex/onnx_serve/api.py index d8133c4f64..e3483efbb5 100644 --- a/pkg/workloads/cortex/onnx_serve/api.py +++ b/pkg/workloads/cortex/onnx_serve/api.py @@ -253,16 +253,17 @@ def exceptions(e): def start(args): - util.extract_zip(os.path.join(args.project_dir, "project.zip"), delete_zip_file=True) api = None try: - packages.install(args.project_dir) ctx = Context(s3_path=args.context, cache_dir=args.cache_dir, workload_id=args.workload_id) api = ctx.apis_id_map[args.api] local_cache["api"] = api local_cache["ctx"] = ctx + util.extract_zip(os.path.join(args.project_dir, ctx.project_key.split("/")[-1])) + packages.install(args.project_dir) + _, 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: diff --git a/pkg/workloads/cortex/tf_api/api.py b/pkg/workloads/cortex/tf_api/api.py index f941742b0f..552dd74334 100644 --- a/pkg/workloads/cortex/tf_api/api.py +++ b/pkg/workloads/cortex/tf_api/api.py @@ -341,15 +341,16 @@ def exceptions(e): def start(args): - util.extract_zip(os.path.join(args.project_dir, "project.zip")) api = None try: - packages.install(args.project_dir) ctx = Context(s3_path=args.context, cache_dir=args.cache_dir, workload_id=args.workload_id) api = ctx.apis_id_map[args.api] local_cache["api"] = api local_cache["ctx"] = ctx + util.extract_zip(os.path.join(args.project_dir, ctx.project_key.split("/")[-1])) + packages.install(args.project_dir) + if api.get("request_handler") is not None: local_cache["request_handler"] = ctx.get_request_handler_impl(api["name"]) except CortexException as e: From b667e7b0f84aa4cee23e988395f793e2d0e70ff3 Mon Sep 17 00:00:00 2001 From: Ivan Zhang Date: Thu, 29 Aug 2019 18:12:02 -0400 Subject: [PATCH 11/22] format --- pkg/operator/workloads/api_workload.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/operator/workloads/api_workload.go b/pkg/operator/workloads/api_workload.go index 3f6970de0e..1ce1808ca9 100644 --- a/pkg/operator/workloads/api_workload.go +++ b/pkg/operator/workloads/api_workload.go @@ -438,7 +438,7 @@ func onnxAPISpec( K8sPodSpec: kcore.PodSpec{ InitContainers: []kcore.Container{ { - Name: downloaderInitContainerName+"-model", + Name: downloaderInitContainerName + "-model", Image: config.Cortex.DownloaderImage, ImagePullPolicy: "Always", Args: []string{ From a663480c933ce04e7f007fdf1efd045e15fc6de6 Mon Sep 17 00:00:00 2001 From: Ivan Zhang Date: Thu, 29 Aug 2019 19:07:43 -0400 Subject: [PATCH 12/22] remove python package --- Makefile | 2 -- cli/cmd/get.go | 4 --- cortex.sh | 2 -- dev/registry.sh | 2 -- docs/cluster/config.md | 1 - docs/cluster/development.md | 1 - manager/install_cortex.sh | 1 - pkg/operator/api/resource/type.go | 22 ++----------- pkg/operator/config/config.go | 52 ++++++++++++++---------------- pkg/operator/workloads/workload.go | 5 ++- 10 files changed, 30 insertions(+), 62 deletions(-) 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/get.go b/cli/cmd/get.go index 9e29a34f40..41ee1a0c9f 100644 --- a/cli/cmd/get.go +++ b/cli/cmd/get.go @@ -518,10 +518,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}, diff --git a/cortex.sh b/cortex.sh index a883e8a270..73819f4871 100755 --- a/cortex.sh +++ b/cortex.sh @@ -150,7 +150,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}" @@ -210,7 +209,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/manager/install_cortex.sh b/manager/install_cortex.sh index 6f001c3809..2c669c77eb 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/operator/api/resource/type.go b/pkg/operator/api/resource/type.go index 852fb47424..904c185862 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,7 +34,6 @@ var ( "unknown", "deployment", "api", - "python_package", } typePlurals = []string{ @@ -48,26 +46,16 @@ var ( "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, } ) @@ -80,10 +68,6 @@ func TypeFromString(s string) Type { if s == typePlurals[i] { return Type(i) } - - if t, ok := typeAcronyms[s]; ok { - return t - } } return UnknownType } 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/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 { From e32317171cfc28a91f1cc0dbf1854db9fc82ea09 Mon Sep 17 00:00:00 2001 From: Ivan Zhang Date: Thu, 29 Aug 2019 23:14:36 -0400 Subject: [PATCH 13/22] address comments --- cli/cmd/deploy.go | 2 +- images/onnx-serve/Dockerfile | 2 +- images/tf-api/Dockerfile | 2 +- pkg/consts/consts.go | 2 - pkg/operator/api/context/apis.go | 3 +- pkg/operator/api/resource/type.go | 6 +++ pkg/operator/context/apis.go | 29 ++----------- pkg/operator/context/context.go | 19 +++----- pkg/operator/endpoints/deploy.go | 11 +++-- pkg/operator/workloads/api_workload.go | 43 +++++++------------ pkg/workloads/cortex/downloader/download.py | 20 ++++++--- pkg/workloads/cortex/lib/context.py | 22 +++------- pkg/workloads/cortex/onnx_serve/api.py | 7 +-- .../{lib/packages.py => onnx_serve/run.sh} | 14 +++--- pkg/workloads/cortex/tf_api/api.py | 7 +-- pkg/workloads/cortex/tf_api/run.sh | 22 ++++++++++ 16 files changed, 97 insertions(+), 114 deletions(-) rename pkg/workloads/cortex/{lib/packages.py => onnx_serve/run.sh} (65%) mode change 100644 => 100755 create mode 100755 pkg/workloads/cortex/tf_api/run.sh diff --git a/cli/cmd/deploy.go b/cli/cmd/deploy.go index 33148bfd83..700cf018ec 100644 --- a/cli/cmd/deploy.go +++ b/cli/cmd/deploy.go @@ -63,7 +63,7 @@ func deploy(force bool, ignoreCache bool) { configBytes, err := ioutil.ReadFile("cortex.yaml") if err != nil { - errors.Exit(errors.Wrap(err, userconfig.ErrorReadConfig().Error())) + errors.Exit(errors.Wrap(err, "cortex.yaml", userconfig.ErrorReadConfig().Error())) } projectPaths, err := files.ListDirRecursive(root, false, diff --git a/images/onnx-serve/Dockerfile b/images/onnx-serve/Dockerfile index 29addd04ab..d8305c7683 100644 --- a/images/onnx-serve/Dockerfile +++ b/images/onnx-serve/Dockerfile @@ -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/tf_api/run.sh"] diff --git a/images/tf-api/Dockerfile b/images/tf-api/Dockerfile index 1b7fb7f8f5..c0c7843b24 100644 --- a/images/tf-api/Dockerfile +++ b/images/tf-api/Dockerfile @@ -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/pkg/consts/consts.go b/pkg/consts/consts.go index acc971bba8..1d08176e31 100644 --- a/pkg/consts/consts.go +++ b/pkg/consts/consts.go @@ -26,8 +26,6 @@ var ( CortexConfigPath = "/configs/cortex" CortexConfigName = "cortex-config" - PackageDir = "packages" - AppsDir = "apps" DeploymentsDir = "deployments" APIsDir = "apis" 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/resource/type.go b/pkg/operator/api/resource/type.go index 904c185862..87aa3ffb01 100644 --- a/pkg/operator/api/resource/type.go +++ b/pkg/operator/api/resource/type.go @@ -57,6 +57,8 @@ var ( VisibleTypes = Types{ APIType, } + + typeAcronyms = map[string]Type{} ) func TypeFromString(s string) Type { @@ -68,6 +70,10 @@ func TypeFromString(s string) Type { if s == typePlurals[i] { return Type(i) } + + if t, ok := typeAcronyms[s]; ok { + return t + } } return UnknownType } diff --git a/pkg/operator/context/apis.go b/pkg/operator/context/apis.go index ec29968260..4aa7c79eb9 100644 --- a/pkg/operator/context/apis.go +++ b/pkg/operator/context/apis.go @@ -18,13 +18,10 @@ 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" s "github.com/cortexlabs/cortex/pkg/lib/strings" "github.com/cortexlabs/cortex/pkg/operator/api/context" "github.com/cortexlabs/cortex/pkg/operator/api/resource" @@ -34,34 +31,17 @@ import ( func getAPIs(config *userconfig.Config, deploymentVersion string, - impls map[string][]byte, + 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 { - 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()) @@ -73,9 +53,8 @@ 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 diff --git a/pkg/operator/context/context.go b/pkg/operator/context/context.go index ec4f2b39fb..0ecc1dd0ce 100644 --- a/pkg/operator/context/context.go +++ b/pkg/operator/context/context.go @@ -24,7 +24,6 @@ import ( "github.com/cortexlabs/cortex/pkg/consts" "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/userconfig" "github.com/cortexlabs/cortex/pkg/operator/config" @@ -35,11 +34,6 @@ func New( projectBytes []byte, ignoreCache bool, ) (*context.Context, error) { - files, err := zip.UnzipMemToMem(projectBytes) - if err != nil { - return nil, err - } - ctx := &context.Context{} ctx.CreatedEpoch = time.Now().Unix() @@ -65,8 +59,14 @@ func New( consts.MetadataDir, ) + 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.StatusPrefix = statusPrefix(ctx.App.Name) - apis, err := getAPIs(userconf, ctx.DeploymentVersion, files) + apis, err := getAPIs(userconf, ctx.DeploymentVersion, ctx.ProjectID) if err != nil { return nil, err @@ -78,11 +78,6 @@ func New( return nil, err } - 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.ID = calculateID(ctx) ctx.Key = ctxKey(ctx.ID, ctx.App.Name) return ctx, nil diff --git a/pkg/operator/endpoints/deploy.go b/pkg/operator/endpoints/deploy.go index 8b65bc5e70..00d3279916 100644 --- a/pkg/operator/endpoints/deploy.go +++ b/pkg/operator/endpoints/deploy.go @@ -47,15 +47,20 @@ func Deploy(w http.ResponseWriter, r *http.Request) { 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 } - userconf, err := userconfig.New("cortex.yaml", configBytes, true) - if err != nil { - RespondError(w, err) + if len(projectBytes) == 0 { + RespondError(w, ErrorFormFileMustBeProvided("project.zip")) return } diff --git a/pkg/operator/workloads/api_workload.go b/pkg/operator/workloads/api_workload.go index 1ce1808ca9..91506bb71c 100644 --- a/pkg/operator/workloads/api_workload.go +++ b/pkg/operator/workloads/api_workload.go @@ -17,6 +17,7 @@ limitations under the License. package workloads import ( + "fmt" "path" kapps "k8s.io/api/apps/v1" @@ -281,23 +282,16 @@ func tfAPISpec( RestartPolicy: "Always", InitContainers: []kcore.Container{ { - Name: downloaderInitContainerName + "-model", + Name: downloaderInitContainerName, Image: config.Cortex.DownloaderImage, ImagePullPolicy: "Always", Args: []string{ - "--download_from=" + ctx.APIs[api.Name].Model, - "--download_to=" + path.Join(consts.EmptyDirMountPath, "model"), - }, - Env: k8s.AWSCredentials(), - VolumeMounts: k8s.DefaultVolumeMounts(), - }, - { - Name: downloaderInitContainerName + "-project", - Image: config.Cortex.DownloaderImage, - ImagePullPolicy: "Always", - Args: []string{ - "--download_from=" + config.AWS.S3Path(ctx.ProjectKey), - "--download_to=" + path.Join(consts.EmptyDirMountPath, "project"), + fmt.Sprintf( + "--download=%s;%s,%s;%s", + ctx.APIs[api.Name].Model, path.Join(consts.EmptyDirMountPath, "model"), + config.AWS.S3Path(ctx.ProjectKey), path.Join(consts.EmptyDirMountPath, "project"), + ), + "--unzip=True", }, Env: k8s.AWSCredentials(), VolumeMounts: k8s.DefaultVolumeMounts(), @@ -438,23 +432,16 @@ func onnxAPISpec( K8sPodSpec: kcore.PodSpec{ InitContainers: []kcore.Container{ { - Name: downloaderInitContainerName + "-model", - Image: config.Cortex.DownloaderImage, - ImagePullPolicy: "Always", - Args: []string{ - "--download_from=" + ctx.APIs[api.Name].Model, - "--download_to=" + path.Join(consts.EmptyDirMountPath, "model"), - }, - Env: k8s.AWSCredentials(), - VolumeMounts: k8s.DefaultVolumeMounts(), - }, - { - Name: downloaderInitContainerName + "-project", + Name: downloaderInitContainerName, Image: config.Cortex.DownloaderImage, ImagePullPolicy: "Always", Args: []string{ - "--download_from=" + config.AWS.S3Path(ctx.ProjectKey), - "--download_to=" + path.Join(consts.EmptyDirMountPath, "project"), + fmt.Sprintf( + "--download=%s;%s,%s;%s", + ctx.APIs[api.Name].Model, path.Join(consts.EmptyDirMountPath, "model"), + config.AWS.S3Path(ctx.ProjectKey), path.Join(consts.EmptyDirMountPath, "project"), + ), + "--unzip=True", }, Env: k8s.AWSCredentials(), VolumeMounts: k8s.DefaultVolumeMounts(), diff --git a/pkg/workloads/cortex/downloader/download.py b/pkg/workloads/cortex/downloader/download.py index 5bb843cb21..236d139c60 100644 --- a/pkg/workloads/cortex/downloader/download.py +++ b/pkg/workloads/cortex/downloader/download.py @@ -13,7 +13,9 @@ # limitations under the License. import argparse +import os +from cortex.lib import util from cortex.lib.storage import S3 from cortex.lib.log import get_logger @@ -21,16 +23,24 @@ 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_args = args.download.split(",") + for download_arg in download_args: + paths = download_arg.split(";") + from_path = paths[0] + to_path = paths[1] + bucket_name, prefix = S3.deconstruct_s3_path(from_path) + s3_client = S3(bucket_name, client_config={}) + s3_client.download(prefix, to_path) + if args.unzip and os.path.basename(from_path).endswith("zip"): + 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="comma separated list of path_to_download_from;path_to_download_to") + na.add_argument("--unzip", default=False, help="unzip contents") 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 0de04a4b35..51e3b8f73b 100644 --- a/pkg/workloads/cortex/lib/context.py +++ b/pkg/workloads/cortex/lib/context.py @@ -90,6 +90,7 @@ def __init__(self, **kwargs): # ID maps self.apis_id_map = ResourceMap(self.apis) if self.apis else None + self.id_map = self.apis_id_map def download_file(self, impl_key, cache_impl_path): if not os.path.isfile(cache_impl_path): @@ -101,30 +102,19 @@ 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): - full_module_name = "{}_{}".format(module_prefix, module_name) - + def load_module(self, impl_path): 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) + impl = imp.load_source(os.path.basename(impl_path).rstrip(".py"), impl_path) except Exception as e: raise UserException("unable to load python file") from e return impl, impl_path - 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( + 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/onnx_serve/api.py b/pkg/workloads/cortex/onnx_serve/api.py index e3483efbb5..8dacf1c441 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, packages, Context, api_utils +from cortex.lib import util, Context, api_utils from cortex.lib.storage import S3 from cortex.lib.log import get_logger from cortex.lib.exceptions import CortexException, UserRuntimeException, UserException @@ -261,13 +261,10 @@ def start(args): local_cache["api"] = api local_cache["ctx"] = ctx - util.extract_zip(os.path.join(args.project_dir, ctx.project_key.split("/")[-1])) - packages.install(args.project_dir) - _, 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: - 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 diff --git a/pkg/workloads/cortex/lib/packages.py b/pkg/workloads/cortex/onnx_serve/run.sh old mode 100644 new mode 100755 similarity index 65% rename from pkg/workloads/cortex/lib/packages.py rename to pkg/workloads/cortex/onnx_serve/run.sh index b7a4687af1..aec7df026a --- a/pkg/workloads/cortex/lib/packages.py +++ b/pkg/workloads/cortex/onnx_serve/run.sh @@ -1,3 +1,5 @@ +#!/bin/bash + # Copyright 2019 Cortex Labs, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,12 +15,8 @@ # limitations under the License. -from subprocess import run - -from cortex.lib.exceptions import UserException, CortexException - +if [ -f "/mnt/project/requirements.txt" ]; then + pip3 install -r /mnt/project/requirements.txt +fi +/usr/bin/python3 /src/cortex/tf_api/api.py $@ -def install(project_path): - completed_process = run("pip3 install -r {}/requirements.txt".format(project_path).split()) - if completed_process.returncode != 0: - raise UserException("installing packages") diff --git a/pkg/workloads/cortex/tf_api/api.py b/pkg/workloads/cortex/tf_api/api.py index 552dd74334..858d3e9036 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, packages, Context, api_utils +from cortex.lib import util, Context, api_utils from cortex.lib.storage import S3 from cortex.lib.log import get_logger from cortex.lib.exceptions import CortexException, UserRuntimeException, UserException @@ -348,11 +348,8 @@ def start(args): local_cache["api"] = api local_cache["ctx"] = ctx - util.extract_zip(os.path.join(args.project_dir, ctx.project_key.split("/")[-1])) - packages.install(args.project_dir) - if api.get("request_handler") is not None: - 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 CortexException as e: e.wrap("error") logger.error(str(e)) diff --git a/pkg/workloads/cortex/tf_api/run.sh b/pkg/workloads/cortex/tf_api/run.sh new file mode 100755 index 0000000000..aec7df026a --- /dev/null +++ b/pkg/workloads/cortex/tf_api/run.sh @@ -0,0 +1,22 @@ +#!/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 $@ + From 7d0cfefa08d461feacf89aa42c96498763d5aae0 Mon Sep 17 00:00:00 2001 From: Ivan Zhang Date: Thu, 29 Aug 2019 23:30:57 -0400 Subject: [PATCH 14/22] format and fix bug --- pkg/workloads/cortex/downloader/download.py | 11 ++++++++--- pkg/workloads/cortex/lib/context.py | 9 +++++---- pkg/workloads/cortex/onnx_serve/api.py | 4 +++- pkg/workloads/cortex/onnx_serve/run.sh | 1 - pkg/workloads/cortex/tf_api/api.py | 4 +++- pkg/workloads/cortex/tf_api/run.sh | 1 - 6 files changed, 19 insertions(+), 11 deletions(-) diff --git a/pkg/workloads/cortex/downloader/download.py b/pkg/workloads/cortex/downloader/download.py index 236d139c60..7f97ae28bc 100644 --- a/pkg/workloads/cortex/downloader/download.py +++ b/pkg/workloads/cortex/downloader/download.py @@ -32,14 +32,19 @@ def start(args): s3_client = S3(bucket_name, client_config={}) s3_client.download(prefix, to_path) if args.unzip and os.path.basename(from_path).endswith("zip"): - util.extract_zip(os.path.join(to_path, os.path.basename(from_path)), delete_zip_file=True) - + 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", required=True, help="comma separated list of path_to_download_from;path_to_download_to") + na.add_argument( + "--download", + required=True, + help="comma separated list of path_to_download_from;path_to_download_to", + ) na.add_argument("--unzip", default=False, help="unzip contents") parser.set_defaults(func=start) diff --git a/pkg/workloads/cortex/lib/context.py b/pkg/workloads/cortex/lib/context.py index 51e3b8f73b..fc9bdddb2c 100644 --- a/pkg/workloads/cortex/lib/context.py +++ b/pkg/workloads/cortex/lib/context.py @@ -102,19 +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, impl_path): + def load_module(self, module_prefix, module_name, impl_path): + full_module_name = "{}_{}".format(module_prefix, module_name) try: - impl = imp.load_source(os.path.basename(impl_path).rstrip(".py"), impl_path) + impl = imp.load_source(full_module_name, impl_path) except Exception as e: raise UserException("unable to load python file") from e - return impl, impl_path + return impl def get_request_handler_impl(self, api_name, project_dir): api = self.apis[api_name] try: impl = self.load_module( - os.path.join(project_dir, api["request_handler"]) + "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/onnx_serve/api.py b/pkg/workloads/cortex/onnx_serve/api.py index 8dacf1c441..b594e65e80 100644 --- a/pkg/workloads/cortex/onnx_serve/api.py +++ b/pkg/workloads/cortex/onnx_serve/api.py @@ -264,7 +264,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: - local_cache["request_handler"] = ctx.get_request_handler_impl(api["name"], args.project_dir) + local_cache["request_handler"] = ctx.get_request_handler_impl( + api["name"], args.project_dir + ) sess = rt.InferenceSession(model_path) local_cache["sess"] = sess diff --git a/pkg/workloads/cortex/onnx_serve/run.sh b/pkg/workloads/cortex/onnx_serve/run.sh index aec7df026a..c7bac75324 100755 --- a/pkg/workloads/cortex/onnx_serve/run.sh +++ b/pkg/workloads/cortex/onnx_serve/run.sh @@ -19,4 +19,3 @@ if [ -f "/mnt/project/requirements.txt" ]; then pip3 install -r /mnt/project/requirements.txt fi /usr/bin/python3 /src/cortex/tf_api/api.py $@ - diff --git a/pkg/workloads/cortex/tf_api/api.py b/pkg/workloads/cortex/tf_api/api.py index 858d3e9036..e20b0c29b1 100644 --- a/pkg/workloads/cortex/tf_api/api.py +++ b/pkg/workloads/cortex/tf_api/api.py @@ -349,7 +349,9 @@ def start(args): local_cache["ctx"] = ctx if api.get("request_handler") is not None: - local_cache["request_handler"] = ctx.get_request_handler_impl(api["name"], args.project_dir) + local_cache["request_handler"] = ctx.get_request_handler_impl( + api["name"], args.project_dir + ) except CortexException as e: e.wrap("error") logger.error(str(e)) diff --git a/pkg/workloads/cortex/tf_api/run.sh b/pkg/workloads/cortex/tf_api/run.sh index aec7df026a..c7bac75324 100755 --- a/pkg/workloads/cortex/tf_api/run.sh +++ b/pkg/workloads/cortex/tf_api/run.sh @@ -19,4 +19,3 @@ if [ -f "/mnt/project/requirements.txt" ]; then pip3 install -r /mnt/project/requirements.txt fi /usr/bin/python3 /src/cortex/tf_api/api.py $@ - From 1df56918a9acb42e521ae6283828faa396793262 Mon Sep 17 00:00:00 2001 From: Ivan Zhang Date: Fri, 30 Aug 2019 12:53:23 -0400 Subject: [PATCH 15/22] address comments --- images/python-packager/Dockerfile | 37 --------------------- pkg/operator/context/apis.go | 18 ---------- pkg/operator/workloads/api_workload.go | 22 +++++++----- pkg/workloads/cortex/downloader/download.py | 6 ++-- pkg/workloads/cortex/onnx_serve/run.sh | 2 +- pkg/workloads/cortex/tf_api/run.sh | 2 +- 6 files changed, 19 insertions(+), 68 deletions(-) delete mode 100644 images/python-packager/Dockerfile 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/pkg/operator/context/apis.go b/pkg/operator/context/apis.go index 4aa7c79eb9..bc452579cf 100644 --- a/pkg/operator/context/apis.go +++ b/pkg/operator/context/apis.go @@ -20,13 +20,11 @@ import ( "bytes" "strings" - "github.com/cortexlabs/cortex/pkg/lib/errors" "github.com/cortexlabs/cortex/pkg/lib/hash" 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" ) func getAPIs(config *userconfig.Config, @@ -59,19 +57,3 @@ func getAPIs(config *userconfig.Config, } 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") - } - } - - return nil -} diff --git a/pkg/operator/workloads/api_workload.go b/pkg/operator/workloads/api_workload.go index 91506bb71c..7a1fdd0465 100644 --- a/pkg/operator/workloads/api_workload.go +++ b/pkg/operator/workloads/api_workload.go @@ -286,10 +286,13 @@ func tfAPISpec( Image: config.Cortex.DownloaderImage, ImagePullPolicy: "Always", Args: []string{ - fmt.Sprintf( - "--download=%s;%s,%s;%s", - ctx.APIs[api.Name].Model, path.Join(consts.EmptyDirMountPath, "model"), - config.AWS.S3Path(ctx.ProjectKey), path.Join(consts.EmptyDirMountPath, "project"), + fmt.Sprintf("--download=%s;%s", + ctx.APIs[api.Name].Model, + path.Join(consts.EmptyDirMountPath, "model"), + ), + fmt.Sprintf("--download=%s;%s", + config.AWS.S3Path(ctx.ProjectKey), + path.Join(consts.EmptyDirMountPath, "project"), ), "--unzip=True", }, @@ -436,10 +439,13 @@ func onnxAPISpec( Image: config.Cortex.DownloaderImage, ImagePullPolicy: "Always", Args: []string{ - fmt.Sprintf( - "--download=%s;%s,%s;%s", - ctx.APIs[api.Name].Model, path.Join(consts.EmptyDirMountPath, "model"), - config.AWS.S3Path(ctx.ProjectKey), path.Join(consts.EmptyDirMountPath, "project"), + fmt.Sprintf("--download=%s;%s", + ctx.APIs[api.Name].Model, + path.Join(consts.EmptyDirMountPath, "model"), + ), + fmt.Sprintf("--download=%s;%s", + config.AWS.S3Path(ctx.ProjectKey), + path.Join(consts.EmptyDirMountPath, "project"), ), "--unzip=True", }, diff --git a/pkg/workloads/cortex/downloader/download.py b/pkg/workloads/cortex/downloader/download.py index 7f97ae28bc..0708fa0df3 100644 --- a/pkg/workloads/cortex/downloader/download.py +++ b/pkg/workloads/cortex/downloader/download.py @@ -23,8 +23,7 @@ def start(args): - download_args = args.download.split(",") - for download_arg in download_args: + for download_arg in args.download: paths = download_arg.split(";") from_path = paths[0] to_path = paths[1] @@ -43,7 +42,8 @@ def main(): na.add_argument( "--download", required=True, - help="comma separated list of path_to_download_from;path_to_download_to", + help="path_to_download_from;path_to_download_to", + action='append' ) na.add_argument("--unzip", default=False, help="unzip contents") parser.set_defaults(func=start) diff --git a/pkg/workloads/cortex/onnx_serve/run.sh b/pkg/workloads/cortex/onnx_serve/run.sh index c7bac75324..802be7913a 100755 --- a/pkg/workloads/cortex/onnx_serve/run.sh +++ b/pkg/workloads/cortex/onnx_serve/run.sh @@ -18,4 +18,4 @@ if [ -f "/mnt/project/requirements.txt" ]; then pip3 install -r /mnt/project/requirements.txt fi -/usr/bin/python3 /src/cortex/tf_api/api.py $@ +/usr/bin/python3 /src/cortex/tf_api/api.py "$@" diff --git a/pkg/workloads/cortex/tf_api/run.sh b/pkg/workloads/cortex/tf_api/run.sh index c7bac75324..802be7913a 100755 --- a/pkg/workloads/cortex/tf_api/run.sh +++ b/pkg/workloads/cortex/tf_api/run.sh @@ -18,4 +18,4 @@ if [ -f "/mnt/project/requirements.txt" ]; then pip3 install -r /mnt/project/requirements.txt fi -/usr/bin/python3 /src/cortex/tf_api/api.py $@ +/usr/bin/python3 /src/cortex/tf_api/api.py "$@" From f864bb9b13b6ed8f988da493d4da037655549a8a Mon Sep 17 00:00:00 2001 From: Ivan Zhang Date: Fri, 30 Aug 2019 13:58:37 -0400 Subject: [PATCH 16/22] use less dangerous download args --- pkg/operator/workloads/api_workload.go | 54 ++++++++++++++------- pkg/workloads/cortex/downloader/download.py | 11 +++-- 2 files changed, 43 insertions(+), 22 deletions(-) diff --git a/pkg/operator/workloads/api_workload.go b/pkg/operator/workloads/api_workload.go index 7a1fdd0465..c4a77707e8 100644 --- a/pkg/operator/workloads/api_workload.go +++ b/pkg/operator/workloads/api_workload.go @@ -17,9 +17,11 @@ limitations under the License. package workloads import ( - "fmt" + "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" @@ -224,6 +226,11 @@ func (aw *APIWorkload) IsFailed(ctx *context.Context) (bool, error) { return false, nil } +type downloadContainerArg struct { + From string `json:"from"` + To string `json:"to"` +} + func tfAPISpec( ctx *context.Context, api *context.API, @@ -251,6 +258,19 @@ 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"), + }, + } + + downloadArgsStr, _ := json.MarshalJSONStr(downloadArgs) + downloadArgsStr = base64.URLEncoding.EncodeToString([]byte(downloadArgsStr)) return k8s.Deployment(&k8s.DeploymentSpec{ Name: internalAPIName(api.Name, ctx.App.Name), Replicas: desiredReplicas, @@ -286,14 +306,7 @@ func tfAPISpec( Image: config.Cortex.DownloaderImage, ImagePullPolicy: "Always", Args: []string{ - fmt.Sprintf("--download=%s;%s", - ctx.APIs[api.Name].Model, - path.Join(consts.EmptyDirMountPath, "model"), - ), - fmt.Sprintf("--download=%s;%s", - config.AWS.S3Path(ctx.ProjectKey), - path.Join(consts.EmptyDirMountPath, "project"), - ), + "--download=" + downloadArgsStr, "--unzip=True", }, Env: k8s.AWSCredentials(), @@ -405,6 +418,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"), + }, + } + + downloadArgsStr, _ := json.MarshalJSONStr(downloadArgs) + downloadArgsStr = base64.URLEncoding.EncodeToString([]byte(downloadArgsStr)) + return k8s.Deployment(&k8s.DeploymentSpec{ Name: internalAPIName(api.Name, ctx.App.Name), Replicas: desiredReplicas, @@ -439,14 +466,7 @@ func onnxAPISpec( Image: config.Cortex.DownloaderImage, ImagePullPolicy: "Always", Args: []string{ - fmt.Sprintf("--download=%s;%s", - ctx.APIs[api.Name].Model, - path.Join(consts.EmptyDirMountPath, "model"), - ), - fmt.Sprintf("--download=%s;%s", - config.AWS.S3Path(ctx.ProjectKey), - path.Join(consts.EmptyDirMountPath, "project"), - ), + "--download=" + downloadArgsStr, "--unzip=True", }, Env: k8s.AWSCredentials(), diff --git a/pkg/workloads/cortex/downloader/download.py b/pkg/workloads/cortex/downloader/download.py index 0708fa0df3..c999ceb8d4 100644 --- a/pkg/workloads/cortex/downloader/download.py +++ b/pkg/workloads/cortex/downloader/download.py @@ -14,6 +14,8 @@ import argparse import os +import base64 +import json from cortex.lib import util from cortex.lib.storage import S3 @@ -23,10 +25,10 @@ def start(args): - for download_arg in args.download: - paths = download_arg.split(";") - from_path = paths[0] - to_path = paths[1] + 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) @@ -43,7 +45,6 @@ def main(): "--download", required=True, help="path_to_download_from;path_to_download_to", - action='append' ) na.add_argument("--unzip", default=False, help="unzip contents") parser.set_defaults(func=start) From 325bbac17a4cddfa805e42064164aa1151fc59b4 Mon Sep 17 00:00:00 2001 From: Ivan Zhang Date: Fri, 30 Aug 2019 13:59:01 -0400 Subject: [PATCH 17/22] format --- pkg/workloads/cortex/downloader/download.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pkg/workloads/cortex/downloader/download.py b/pkg/workloads/cortex/downloader/download.py index c999ceb8d4..546612897e 100644 --- a/pkg/workloads/cortex/downloader/download.py +++ b/pkg/workloads/cortex/downloader/download.py @@ -41,11 +41,7 @@ def start(args): def main(): parser = argparse.ArgumentParser() na = parser.add_argument_group("required named arguments") - na.add_argument( - "--download", - required=True, - help="path_to_download_from;path_to_download_to", - ) + na.add_argument("--download", required=True, help="path_to_download_from;path_to_download_to") na.add_argument("--unzip", default=False, help="unzip contents") parser.set_defaults(func=start) From ed4813871f8fadb5b54e384eb8f3d6bd7fb1e766 Mon Sep 17 00:00:00 2001 From: Ivan Zhang Date: Fri, 30 Aug 2019 14:06:38 -0400 Subject: [PATCH 18/22] nit --- pkg/operator/context/apis.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pkg/operator/context/apis.go b/pkg/operator/context/apis.go index bc452579cf..1cb0c1698b 100644 --- a/pkg/operator/context/apis.go +++ b/pkg/operator/context/apis.go @@ -27,10 +27,7 @@ import ( "github.com/cortexlabs/cortex/pkg/operator/api/userconfig" ) -func getAPIs(config *userconfig.Config, - deploymentVersion string, - projectID string, -) (context.APIs, error) { +func getAPIs(config *userconfig.Config, deploymentVersion string, projectID string) (context.APIs, error) { apis := context.APIs{} for _, apiConfig := range config.APIs { From 0f122eae2f418736268e13bfae97333da4d11e80 Mon Sep 17 00:00:00 2001 From: Ivan Zhang Date: Fri, 30 Aug 2019 14:19:24 -0400 Subject: [PATCH 19/22] address comments --- images/onnx-serve/Dockerfile | 2 +- pkg/operator/workloads/api_workload.go | 17 +++++++++-------- pkg/workloads/cortex/downloader/download.py | 3 +-- pkg/workloads/cortex/onnx_serve/run.sh | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/images/onnx-serve/Dockerfile b/images/onnx-serve/Dockerfile index d8305c7683..0f238af080 100644 --- a/images/onnx-serve/Dockerfile +++ b/images/onnx-serve/Dockerfile @@ -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 ["/src/cortex/tf_api/run.sh"] +ENTRYPOINT ["/src/cortex/onnx_serve/run.sh"] diff --git a/pkg/operator/workloads/api_workload.go b/pkg/operator/workloads/api_workload.go index c4a77707e8..53b049cb47 100644 --- a/pkg/operator/workloads/api_workload.go +++ b/pkg/operator/workloads/api_workload.go @@ -227,8 +227,9 @@ func (aw *APIWorkload) IsFailed(ctx *context.Context) (bool, error) { } type downloadContainerArg struct { - From string `json:"from"` - To string `json:"to"` + From string `json:"from"` + To string `json:"to"` + Unzip bool `json:"unzip"` } func tfAPISpec( @@ -264,8 +265,9 @@ func tfAPISpec( To: path.Join(consts.EmptyDirMountPath, "model"), }, { - From: config.AWS.S3Path(ctx.ProjectKey), - To: path.Join(consts.EmptyDirMountPath, "project"), + From: config.AWS.S3Path(ctx.ProjectKey), + To: path.Join(consts.EmptyDirMountPath, "project"), + Unzip: true, }, } @@ -307,7 +309,6 @@ func tfAPISpec( ImagePullPolicy: "Always", Args: []string{ "--download=" + downloadArgsStr, - "--unzip=True", }, Env: k8s.AWSCredentials(), VolumeMounts: k8s.DefaultVolumeMounts(), @@ -424,8 +425,9 @@ func onnxAPISpec( To: path.Join(consts.EmptyDirMountPath, "model"), }, { - From: config.AWS.S3Path(ctx.ProjectKey), - To: path.Join(consts.EmptyDirMountPath, "project"), + From: config.AWS.S3Path(ctx.ProjectKey), + To: path.Join(consts.EmptyDirMountPath, "project"), + Unzip: true, }, } @@ -467,7 +469,6 @@ func onnxAPISpec( ImagePullPolicy: "Always", Args: []string{ "--download=" + downloadArgsStr, - "--unzip=True", }, Env: k8s.AWSCredentials(), VolumeMounts: k8s.DefaultVolumeMounts(), diff --git a/pkg/workloads/cortex/downloader/download.py b/pkg/workloads/cortex/downloader/download.py index 546612897e..3f654b3619 100644 --- a/pkg/workloads/cortex/downloader/download.py +++ b/pkg/workloads/cortex/downloader/download.py @@ -32,7 +32,7 @@ def start(args): bucket_name, prefix = S3.deconstruct_s3_path(from_path) s3_client = S3(bucket_name, client_config={}) s3_client.download(prefix, to_path) - if args.unzip and os.path.basename(from_path).endswith("zip"): + if download_arg["unzip"] and os.path.basename(from_path).endswith("zip"): util.extract_zip( os.path.join(to_path, os.path.basename(from_path)), delete_zip_file=True ) @@ -42,7 +42,6 @@ def main(): parser = argparse.ArgumentParser() na = parser.add_argument_group("required named arguments") na.add_argument("--download", required=True, help="path_to_download_from;path_to_download_to") - na.add_argument("--unzip", default=False, help="unzip contents") parser.set_defaults(func=start) args = parser.parse_args() diff --git a/pkg/workloads/cortex/onnx_serve/run.sh b/pkg/workloads/cortex/onnx_serve/run.sh index 802be7913a..729c676a76 100755 --- a/pkg/workloads/cortex/onnx_serve/run.sh +++ b/pkg/workloads/cortex/onnx_serve/run.sh @@ -18,4 +18,4 @@ if [ -f "/mnt/project/requirements.txt" ]; then pip3 install -r /mnt/project/requirements.txt fi -/usr/bin/python3 /src/cortex/tf_api/api.py "$@" +/usr/bin/python3 /src/cortex/onnx_serve/api.py "$@" From 1f7db433f7c39d8ea1e93417cdbc79808a7e7cac Mon Sep 17 00:00:00 2001 From: Ivan Zhang Date: Fri, 30 Aug 2019 14:29:11 -0400 Subject: [PATCH 20/22] update gpu --- images/onnx-serve-gpu/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/onnx-serve-gpu/Dockerfile b/images/onnx-serve-gpu/Dockerfile index b05eef038e..1172306cb5 100644 --- a/images/onnx-serve-gpu/Dockerfile +++ b/images/onnx-serve-gpu/Dockerfile @@ -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"] From 72bb6a0e849d14fd01acf736ca44f99b1b90226f Mon Sep 17 00:00:00 2001 From: Ivan Zhang Date: Fri, 30 Aug 2019 14:32:44 -0400 Subject: [PATCH 21/22] address comments --- pkg/operator/workloads/api_workload.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pkg/operator/workloads/api_workload.go b/pkg/operator/workloads/api_workload.go index 53b049cb47..f34e251634 100644 --- a/pkg/operator/workloads/api_workload.go +++ b/pkg/operator/workloads/api_workload.go @@ -271,8 +271,8 @@ func tfAPISpec( }, } - downloadArgsStr, _ := json.MarshalJSONStr(downloadArgs) - downloadArgsStr = base64.URLEncoding.EncodeToString([]byte(downloadArgsStr)) + downloadArgsBytes, _ := json.Marshal(downloadArgs) + downloadArgsStr := base64.URLEncoding.EncodeToString(downloadArgsBytes) return k8s.Deployment(&k8s.DeploymentSpec{ Name: internalAPIName(api.Name, ctx.App.Name), Replicas: desiredReplicas, @@ -431,9 +431,8 @@ func onnxAPISpec( }, } - downloadArgsStr, _ := json.MarshalJSONStr(downloadArgs) - downloadArgsStr = base64.URLEncoding.EncodeToString([]byte(downloadArgsStr)) - + downloadArgsBytes, _ := json.Marshal(downloadArgs) + downloadArgsStr := base64.URLEncoding.EncodeToString(downloadArgsBytes) return k8s.Deployment(&k8s.DeploymentSpec{ Name: internalAPIName(api.Name, ctx.App.Name), Replicas: desiredReplicas, From 6be5633e0a22662c959c860b12c9daa8c658959a Mon Sep 17 00:00:00 2001 From: Ivan Zhang Date: Fri, 30 Aug 2019 14:39:32 -0400 Subject: [PATCH 22/22] address comment --- pkg/workloads/cortex/downloader/download.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/workloads/cortex/downloader/download.py b/pkg/workloads/cortex/downloader/download.py index 3f654b3619..5be0e4501a 100644 --- a/pkg/workloads/cortex/downloader/download.py +++ b/pkg/workloads/cortex/downloader/download.py @@ -32,7 +32,7 @@ def start(args): 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"] and os.path.basename(from_path).endswith("zip"): + if download_arg["unzip"]: util.extract_zip( os.path.join(to_path, os.path.basename(from_path)), delete_zip_file=True ) @@ -41,7 +41,11 @@ def start(args): def main(): parser = argparse.ArgumentParser() na = parser.add_argument_group("required named arguments") - na.add_argument("--download", required=True, help="path_to_download_from;path_to_download_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()