Skip to content

Commit f075695

Browse files
committed
Round #3
1 parent d5a74ec commit f075695

File tree

11 files changed

+719
-11
lines changed

11 files changed

+719
-11
lines changed

cli/cmd/local.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,11 @@ var localLogs = &cobra.Command{
211211
Long: "local an application.",
212212
Args: cobra.ExactArgs(1),
213213
Run: func(cmd *cobra.Command, args []string) {
214-
path, err := files.GetAbsPath(args[0])
214+
paths, err := files.ListDirRecursive(args[0], true)
215215
if err != nil {
216216
panic(err)
217217
}
218-
fmt.Println(path)
218+
debug.Pp(paths)
219219
// containers := GetContainerByAPI(args[0])
220220
// containerIDs := []string{}
221221
// for _, container := range containers {

cli/local/config.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
Copyright 2020 Cortex Labs, Inc.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package local
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"os"
23+
"path/filepath"
24+
"strings"
25+
26+
"github.com/cortexlabs/cortex/pkg/lib/errors"
27+
"github.com/cortexlabs/cortex/pkg/lib/exit"
28+
s "github.com/cortexlabs/cortex/pkg/lib/strings"
29+
dockerclient "github.com/docker/docker/client"
30+
"github.com/mitchellh/go-homedir"
31+
)
32+
33+
var _cachedDockerClient *dockerclient.Client
34+
35+
var CWD string
36+
var LocalDir string
37+
var LocalWorkspace string
38+
39+
func init() {
40+
cwd, err := os.Getwd()
41+
if err != nil {
42+
err := errors.Wrap(err, "unable to determine current working directory")
43+
exit.Error(err)
44+
}
45+
CWD = s.EnsureSuffix(cwd, "/")
46+
47+
homeDir, err := homedir.Dir()
48+
if err != nil {
49+
err := errors.Wrap(err, "unable to determine home directory")
50+
exit.Error(err)
51+
}
52+
53+
LocalDir = filepath.Join(homeDir, ".cortex")
54+
err = os.MkdirAll(LocalDir, os.ModePerm)
55+
if err != nil {
56+
err := errors.Wrap(err, "unable to write to home directory", LocalDir)
57+
exit.Error(err)
58+
}
59+
60+
LocalWorkspace = filepath.Join(LocalDir, "local_workspace")
61+
fmt.Println(LocalWorkspace)
62+
err = os.MkdirAll(LocalWorkspace, os.ModePerm)
63+
if err != nil {
64+
err := errors.Wrap(err, "unable to write to home directory", LocalDir)
65+
exit.Error(err)
66+
}
67+
}
68+
69+
func DockerClient() *dockerclient.Client {
70+
if _cachedDockerClient != nil {
71+
return _cachedDockerClient
72+
}
73+
74+
var err error
75+
_cachedDockerClient, err = dockerclient.NewClientWithOpts(dockerclient.FromEnv)
76+
if err != nil {
77+
exit.Error(wrapDockerError(err))
78+
}
79+
80+
_cachedDockerClient.NegotiateAPIVersion(context.Background())
81+
return _cachedDockerClient
82+
}
83+
84+
func wrapDockerError(err error) error {
85+
if dockerclient.IsErrConnectionFailed(err) {
86+
return ErrorConnectToDockerDaemon()
87+
}
88+
89+
if strings.Contains(strings.ToLower(err.Error()), "permission denied") {
90+
return ErrorDockerPermissions(err)
91+
}
92+
93+
return errors.WithStack(err)
94+
}

cli/local/deploy.go

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
/*
2+
Copyright 2020 Cortex Labs, Inc.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package local
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"os"
23+
"path/filepath"
24+
25+
"github.com/cortexlabs/cortex/pkg/lib/debug"
26+
"github.com/cortexlabs/cortex/pkg/lib/exit"
27+
"github.com/cortexlabs/cortex/pkg/lib/files"
28+
"github.com/cortexlabs/cortex/pkg/lib/k8s"
29+
"github.com/cortexlabs/cortex/pkg/lib/msgpack"
30+
"github.com/cortexlabs/cortex/pkg/types/spec"
31+
"github.com/cortexlabs/cortex/pkg/types/userconfig"
32+
"github.com/docker/docker/api/types"
33+
dockertypes "github.com/docker/docker/api/types"
34+
"github.com/docker/docker/api/types/container"
35+
"github.com/docker/docker/api/types/filters"
36+
"github.com/docker/docker/api/types/mount"
37+
"github.com/docker/go-connections/nat"
38+
)
39+
40+
func GetContainerByAPI(apiName string) ([]dockertypes.Container, error) {
41+
dargs := filters.NewArgs()
42+
dargs.Add("label", "cortex=true")
43+
dargs.Add("label", "apiName="+apiName)
44+
45+
containers, err := DockerClient().ContainerList(context.Background(), types.ContainerListOptions{
46+
Filters: dargs,
47+
})
48+
if err != nil {
49+
return nil, err
50+
}
51+
52+
return containers, nil
53+
}
54+
55+
// func deploymentSpec(api *spec.API, prevDeployment *kapps.Deployment) *kapps.Deployment {
56+
// switch api.Predictor.Type {
57+
// case userconfig.TensorFlowPredictorType:
58+
// return tfAPISpec(api, prevDeployment)
59+
// case userconfig.ONNXPredictorType:
60+
// return onnxAPISpec(api, prevDeployment)
61+
// case userconfig.PythonPredictorType:
62+
// return pythonAPISpec(api, prevDeployment)
63+
// default:
64+
// return nil // unexpected
65+
// }
66+
// }
67+
68+
func DeployContainers(api *spec.API) error {
69+
hostConfig := &container.HostConfig{
70+
PortBindings: nat.PortMap{
71+
"8888/tcp": []nat.PortBinding{
72+
{
73+
// HostPort: "8888",
74+
},
75+
},
76+
},
77+
Mounts: []mount.Mount{
78+
{
79+
Type: mount.TypeBind,
80+
Source: CWD,
81+
Target: "/mnt/project",
82+
},
83+
{
84+
Type: mount.TypeBind,
85+
Source: filepath.Join(LocalWorkspace),
86+
Target: "/mnt/workspace",
87+
},
88+
},
89+
}
90+
91+
containerConfig := &container.Config{
92+
Image: "cortexlabs/python-serve:latest",
93+
Tty: true,
94+
AttachStdout: true,
95+
AttachStderr: true,
96+
Env: []string{
97+
"CORTEX_VERSION=master",
98+
"CORTEX_SERVING_PORT=8888",
99+
"CORTEX_BASE_DIR=" + "/mnt/workspace",
100+
"CORTEX_CACHE_DIR=" + "/mnt/cache",
101+
"CORTEX_API_SPEC=" + filepath.Join("/mnt/workspace", api.Key),
102+
"CORTEX_PROJECT_DIR=" + "/mnt/project",
103+
"CORTEX_WORKERS_PER_REPLICA=1",
104+
"CORTEX_MAX_WORKER_CONCURRENCY=10",
105+
"CORTEX_SO_MAX_CONN=10",
106+
"CORTEX_THREADS_PER_WORKER=1",
107+
"AWS_ACCESS_KEY_ID=" + os.Getenv("AWS_ACCESS_KEY_ID"),
108+
"AWS_SECRET_ACCESS_KEY=" + os.Getenv("AWS_SECRET_ACCESS_KEY"),
109+
},
110+
ExposedPorts: nat.PortSet{
111+
"8888/tcp": struct{}{},
112+
},
113+
Labels: map[string]string{
114+
"cortex": "true",
115+
"apiID": api.ID,
116+
"apiName": api.Name,
117+
"deploymentID": api.DeploymentID,
118+
},
119+
}
120+
debug.Pp(containerConfig.Labels)
121+
containerInfo, err := DockerClient().ContainerCreate(context.Background(), containerConfig, hostConfig, nil, "")
122+
if err != nil {
123+
return err
124+
}
125+
126+
err = DockerClient().ContainerStart(context.Background(), containerInfo.ID, dockertypes.ContainerStartOptions{})
127+
if err != nil {
128+
return err
129+
}
130+
131+
return nil
132+
}
133+
134+
// func ONNXSpec(api *spec.API) error {
135+
// modelPath := *api.Predictor.Model
136+
137+
// if strings.HasPrefix()
138+
139+
// hostConfig := &container.HostConfig{
140+
// PortBindings: nat.PortMap{
141+
// "8888/tcp": []nat.PortBinding{
142+
// {
143+
// // HostPort: "8888",
144+
// },
145+
// },
146+
// },
147+
// Mounts: []mount.Mount{
148+
// {
149+
// Type: mount.TypeBind,
150+
// Source: CWD,
151+
// Target: "/mnt/project",
152+
// },
153+
// {
154+
// Type: mount.TypeBind,
155+
// Source: filepath.Join(LocalWorkspace),
156+
// Target: "/mnt/workspace",
157+
// },
158+
// },
159+
// }
160+
161+
// containerConfig := &container.Config{
162+
// Image: "cortexlabs/onnx-serve:latest",
163+
// Tty: true,
164+
// AttachStdout: true,
165+
// AttachStderr: true,
166+
// Env: []string{
167+
// "CORTEX_VERSION=master",
168+
// "CORTEX_SERVING_PORT=8888",
169+
// "CORTEX_BASE_DIR=" + "/mnt/workspace",
170+
// "CORTEX_CACHE_DIR=" + "/mnt/cache",
171+
// "CORTEX_API_SPEC=" + filepath.Join("/mnt/workspace", api.Key),
172+
// "CORTEX_PROJECT_DIR=" + "/mnt/project",
173+
// "CORTEX_WORKERS_PER_REPLICA=1",
174+
// "CORTEX_MAX_WORKER_CONCURRENCY=10",
175+
// "CORTEX_SO_MAX_CONN=10",
176+
// "CORTEX_THREADS_PER_WORKER=1",
177+
// "AWS_ACCESS_KEY_ID=" + os.Getenv("AWS_ACCESS_KEY_ID"),
178+
// "AWS_SECRET_ACCESS_KEY=" + os.Getenv("AWS_SECRET_ACCESS_KEY"),
179+
// },
180+
// ExposedPorts: nat.PortSet{
181+
// "8888/tcp": struct{}{},
182+
// },
183+
// Labels: map[string]string{
184+
// "cortex": "true",
185+
// "apiID": api.ID,
186+
// "apiName": api.Name,
187+
// "deploymentID": api.DeploymentID,
188+
// },
189+
// }
190+
// debug.Pp(containerConfig.Labels)
191+
// containerInfo, err := DockerClient().ContainerCreate(context.Background(), containerConfig, hostConfig, nil, "")
192+
// if err != nil {
193+
// return err
194+
// }
195+
196+
// err = DockerClient().ContainerStart(context.Background(), containerInfo.ID, dockertypes.ContainerStartOptions{})
197+
// if err != nil {
198+
// return err
199+
// }
200+
201+
// return nil
202+
// }
203+
204+
func DeleteContainers(apiName string) error {
205+
containers, err := GetContainerByAPI(apiName)
206+
if err != nil {
207+
return err
208+
}
209+
210+
for _, container := range containers {
211+
attemptErr := DockerClient().ContainerRemove(context.Background(), container.ID, dockertypes.ContainerRemoveOptions{
212+
RemoveVolumes: true,
213+
Force: true,
214+
})
215+
if attemptErr != nil {
216+
err = attemptErr
217+
}
218+
}
219+
if err != nil {
220+
return err
221+
}
222+
return nil
223+
}
224+
225+
func UpdateAPI(apiConfig *userconfig.API, projectID string) (*spec.API, string, error) {
226+
containers, err := GetContainerByAPI(apiConfig.Name)
227+
if err != nil {
228+
return nil, "", err
229+
}
230+
231+
deploymentID := k8s.RandomName()
232+
if len(containers) > 0 && containers[0].Labels["deploymentID"] != "" {
233+
deploymentID = containers[0].Labels["deploymentID"]
234+
}
235+
236+
api := spec.GetAPISpec(apiConfig, projectID, deploymentID)
237+
238+
if len(containers) == 0 {
239+
apiBytes, err := msgpack.Marshal(api)
240+
if err != nil {
241+
exit.Error(err)
242+
}
243+
os.MkdirAll(files.ParentDir(filepath.Join(LocalWorkspace, api.Key)), os.ModePerm)
244+
err = files.WriteFile(apiBytes, filepath.Join(LocalWorkspace, api.Key))
245+
246+
if err := DeployContainers(api); err != nil {
247+
DeleteContainers(api.Name)
248+
return nil, "", err
249+
}
250+
return api, fmt.Sprintf("creating %s", api.Name), nil
251+
}
252+
253+
prevContainerLabels := containers[0].Labels
254+
debug.Pp(prevContainerLabels)
255+
if prevContainerLabels["apiName"] == api.Name && prevContainerLabels["apiID"] == api.ID {
256+
return api, fmt.Sprintf("%s is up to date", api.Name), nil
257+
}
258+
259+
apiBytes, err := msgpack.Marshal(api)
260+
if err != nil {
261+
exit.Error(err)
262+
}
263+
os.MkdirAll(files.ParentDir(filepath.Join(LocalWorkspace, api.Key)), os.ModePerm)
264+
err = files.WriteFile(apiBytes, filepath.Join(LocalWorkspace, api.Key))
265+
266+
DeleteContainers(api.Name)
267+
if err := DeployContainers(api); err != nil {
268+
panic("here")
269+
fmt.Println("here")
270+
DeleteContainers(api.Name)
271+
return nil, "", err
272+
}
273+
274+
return api, fmt.Sprintf("updating %s", api.Name), nil
275+
}
276+
277+
func DeleteAPI(apiName string, keepCache bool) error {
278+
return DeleteContainers(apiName)
279+
}

0 commit comments

Comments
 (0)