Skip to content

Commit

Permalink
feat(v1): copy the requirements content if possible (#1738)
Browse files Browse the repository at this point in the history
Signed-off-by: Keming <kemingyang@tensorchord.ai>
  • Loading branch information
kemingy committed Aug 14, 2023
1 parent 7441e38 commit be78326
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 15 deletions.
4 changes: 1 addition & 3 deletions pkg/builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"context"
"io"
"os"
"path/filepath"
"strings"

"github.com/cockroachdb/errors"
Expand Down Expand Up @@ -160,12 +159,11 @@ func (b generalBuilder) Interpret() error {
}

func (b generalBuilder) Compile(ctx context.Context) (*llb.Definition, error) {
envName := filepath.Base(b.BuildContextDir)
platform, err := parsePlatform(b.Platform)
if err != nil {
return nil, err
}
def, err := b.graph.Compile(ctx, envName, b.PubKeyPath, platform, b.Options.ProgressMode)
def, err := b.graph.Compile(ctx, b.BuildContextDir, b.PubKeyPath, platform, b.Options.ProgressMode)
if err != nil {
return nil, errors.Wrap(err, "failed to compile build.envd")
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/lang/ir/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
)

type Graph interface {
Compile(ctx context.Context, envName string, pub string, platform *ocispecs.Platform, progressMode string) (*llb.Definition, error)
Compile(ctx context.Context, envPath string, pub string, platform *ocispecs.Platform, progressMode string) (*llb.Definition, error)

graphDebugger
graphVisitor
Expand Down
5 changes: 3 additions & 2 deletions pkg/lang/ir/v0/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,14 @@ func (g generalGraph) GetPlatform() *ocispecs.Platform {
return g.Platform
}

func (g *generalGraph) Compile(ctx context.Context, envName string, pub string, platform *ocispecs.Platform, progressMode string) (*llb.Definition, error) {
func (g *generalGraph) Compile(ctx context.Context, envPath string, pub string, platform *ocispecs.Platform, progressMode string) (*llb.Definition, error) {
w, err := compileui.New(ctx, os.Stdout, progressMode)
if err != nil {
return nil, errors.Wrap(err, "failed to create compileui")
}
g.Writer = w
g.EnvironmentName = envName
g.EnvironmentPath = envPath
g.EnvironmentName = filepath.Base(envPath)
g.PublicKeyPath = pub
g.Platform = platform

Expand Down
2 changes: 2 additions & 0 deletions pkg/lang/ir/v0/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ type generalGraph struct {
// It is the BaseDir(BuildContextDir)
// e.g. mnist, streamlit-mnist
EnvironmentName string
// EnvironmentPath is the full path of this environment.
EnvironmentPath string

ir.RuntimeGraph

Expand Down
5 changes: 3 additions & 2 deletions pkg/lang/ir/v1/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func (g generalGraph) GetPlatform() *ocispecs.Platform {
return g.Platform
}

func (g *generalGraph) Compile(ctx context.Context, envName string, pub string, platform *ocispecs.Platform, progressMode string) (*llb.Definition, error) {
func (g *generalGraph) Compile(ctx context.Context, envPath string, pub string, platform *ocispecs.Platform, progressMode string) (*llb.Definition, error) {
w, err := compileui.New(ctx, os.Stdout, progressMode)
if err != nil {
return nil, errors.Wrap(err, "failed to create compileui")
Expand All @@ -120,7 +120,8 @@ func (g *generalGraph) Compile(ctx context.Context, envName string, pub string,
}

g.Writer = w
g.EnvironmentName = envName
g.EnvironmentPath = envPath
g.EnvironmentName = filepath.Base(envPath)
g.PublicKeyPath = pub
g.Platform = platform
if c.Builder == types.BuilderTypeMoby {
Expand Down
26 changes: 19 additions & 7 deletions pkg/lang/ir/v1/python.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,25 @@ func (g generalGraph) compilePyPIPackages(root llb.State) llb.State {
logrus.WithField("file", *g.RequirementsFile).
Debug("Configure pip install requirements statements")
root = root.Dir(g.getWorkingDir())
run := root.
Run(llb.Shlexf("python -m pip install -r %s", *g.RequirementsFile),
llb.WithCustomNamef("pip install -r %s", *g.RequirementsFile))
run.AddMount(cacheDir, cache,
llb.AsPersistentCacheDir(g.CacheID(cacheDir), llb.CacheMountShared), llb.SourcePath("/cache/pip"))
run.AddMount(g.getWorkingDir(), llb.Local(flag.FlagBuildContext))
root = run.Root()
dependencies, safeToCopy := g.IsRequirementsFileSafeToCopyContent()
logrus.WithField("safeToCopy", safeToCopy).WithField("dependencies", dependencies).
Debug("Is requirements file safe to copy")
if safeToCopy {
// avoid mounting host directory to make it cache friendly
root = root.Run(llb.Shlexf(
"python -m pip install %s",
strings.Join(dependencies, " ")),
llb.WithCustomNamef("[internal] pip install from %s with %s", *g.RequirementsFile, strings.Join(dependencies, " ")),
).Root()
} else {
run := root.
Run(llb.Shlexf("python -m pip install -r %s", *g.RequirementsFile),
llb.WithCustomNamef("[internal] pip install -r %s", *g.RequirementsFile))
run.AddMount(cacheDir, cache,
llb.AsPersistentCacheDir(g.CacheID(cacheDir), llb.CacheMountShared), llb.SourcePath("/cache/pip"))
run.AddMount(g.getWorkingDir(), llb.Local(flag.FlagBuildContext))
root = run.Root()
}
}

if len(g.PythonWheels) > 0 {
Expand Down
2 changes: 2 additions & 0 deletions pkg/lang/ir/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ type generalGraph struct {
// It is the BaseDir(BuildContextDir)
// e.g. mnist, streamlit-mnist
EnvironmentName string
// EnvironmentPath is the full path of this environment.
EnvironmentPath string

// (v1) disable `merge` op for `moby` builder
// check https://github.com/tensorchord/envd/issues/1693
Expand Down
48 changes: 48 additions & 0 deletions pkg/lang/ir/v1/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@
package v1

import (
"bufio"
"bytes"
"crypto/md5"
"encoding/gob"
"encoding/hex"
"encoding/json"
"fmt"
"os"
"os/user"
"path/filepath"
"regexp"
"runtime"
"strconv"
Expand Down Expand Up @@ -175,3 +178,48 @@ func (g generalGraph) GeneralGraphFromLabel(label []byte) (ir.Graph, error) {
}
return &newg, nil
}

// IsRequirementsFileSafeToCopyContent returns true and the dependencies in this file if the
// requirements file is safe to copy. Otherwise, it returns false and empty dependencies.
// check https://github.com/tensorchord/envd/issues/1629
func (g generalGraph) IsRequirementsFileSafeToCopyContent() (dependencies []string, safe bool) {
if g.RequirementsFile == nil {
return
}
filePath := filepath.Join(g.EnvironmentPath, *g.RequirementsFile)
file, err := os.Open(filePath)
if err != nil {
logrus.WithError(err).Debugf("failed to open requirements file: %s", filePath)
return
}
defer file.Close()

forbidden := []string{
"-e",
"-r",
"-c",
"--find-links",
}

scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "#") {
continue
}
for _, f := range forbidden {
if strings.Contains(line, f) {
logrus.Debugf("requirements file contains %s, which is not safe to copy", f)
return []string{}, false
}
}
exist, err := fileutil.FileExists(line)
// has a local file to install
if err == nil && exist {
logrus.Debugf("requirements file contains local file %s, which is not safe to copy", line)
return []string{}, false
}
dependencies = append(dependencies, line)
}
return dependencies, true
}

0 comments on commit be78326

Please sign in to comment.