Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add function YAML config to ignore paths during shrinkwrap #888

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const AdditionalPackageBuildArg = "ADDITIONAL_PACKAGE"

// BuildImage construct Docker image from function parameters
// TODO: refactor signature to a struct to simplify the length of the method header
func BuildImage(image string, handler string, functionName string, language string, nocache bool, squash bool, shrinkwrap bool, buildArgMap map[string]string, buildOptions []string, tagMode schema.BuildFormat, buildLabelMap map[string]string, quietBuild bool, copyExtraPaths []string) error {
func BuildImage(image string, handler string, functionName string, language string, nocache bool, squash bool, shrinkwrap bool, buildArgMap map[string]string, buildOptions []string, tagMode schema.BuildFormat, buildLabelMap map[string]string, quietBuild bool, copyExtraPaths []string, ignorePaths []string) error {

if stack.IsValidTemplate(language) {
pathToTemplateYAML := fmt.Sprintf("./template/%s/template.yml", language)
Expand All @@ -48,7 +48,7 @@ func BuildImage(image string, handler string, functionName string, language stri
return fmt.Errorf("building %s, %s is an invalid path", imageName, handler)
}

tempPath, buildErr := createBuildContext(functionName, handler, language, isLanguageTemplate(language), langTemplate.HandlerFolder, copyExtraPaths)
tempPath, buildErr := createBuildContext(functionName, handler, language, isLanguageTemplate(language), langTemplate.HandlerFolder, copyExtraPaths, ignorePaths)
fmt.Printf("Building: %s with %s template. Please wait..\n", imageName, language)
if buildErr != nil {
return buildErr
Expand Down Expand Up @@ -184,7 +184,7 @@ func isRunningInCI() bool {
}

// createBuildContext creates temporary build folder to perform a Docker build with language template
func createBuildContext(functionName string, handler string, language string, useFunction bool, handlerFolder string, copyExtraPaths []string) (string, error) {
func createBuildContext(functionName string, handler string, language string, useFunction bool, handlerFolder string, copyExtraPaths []string, ignorePaths []string) (string, error) {
tempPath := fmt.Sprintf("./build/%s/", functionName)
fmt.Printf("Clearing temporary build folder: %s\n", tempPath)

Expand Down Expand Up @@ -232,15 +232,21 @@ func createBuildContext(functionName string, handler string, language string, us
return tempPath, readErr
}

functionIgnorePaths := []string{}
for _, ignorePath := range ignorePaths {
functionIgnorePaths = append(functionIgnorePaths, filepath.Clean(path.Join(functionPath, ignorePath)))
}

for _, info := range infos {
switch info.Name() {
case "build", "template":
fmt.Printf("Skipping \"%s\" folder\n", info.Name())
continue
default:
copyErr := CopyFiles(
copyErr := CopyFilesWithIgnorePaths(
filepath.Clean(path.Join(handler, info.Name())),
filepath.Clean(path.Join(functionPath, info.Name())),
functionIgnorePaths,
)

if copyErr != nil {
Expand All @@ -257,9 +263,10 @@ func createBuildContext(functionName string, handler string, language string, us
// Note that if useFunction is false, ie is a `dockerfile` template, then
// functionPath == tempPath, the docker build context, not the `function` handler folder
// inside the docker build context
copyErr := CopyFiles(
copyErr := CopyFilesWithIgnorePaths(
extraPathAbs,
filepath.Clean(path.Join(functionPath, extraPath)),
functionIgnorePaths,
)

if copyErr != nil {
Expand Down
19 changes: 16 additions & 3 deletions builder/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,34 @@ import (

// CopyFiles copies files from src to destination.
func CopyFiles(src, dest string) error {
return CopyFilesWithIgnorePaths(src, dest, nil)
}

// CopyFilesWithIgnorePaths copies files from src to destination,
// but ignores files or directories specified by ignorePaths
func CopyFilesWithIgnorePaths(src, dest string, ignorePaths []string) error {
info, err := os.Stat(src)
if err != nil {
return err
}

for _, ignorePath := range ignorePaths {
if dest == ignorePath {
return nil
}
}

if info.IsDir() {
debugPrint(fmt.Sprintf("Creating directory: %s at %s", info.Name(), dest))
return copyDir(src, dest)
return copyDir(src, dest, ignorePaths)
}

debugPrint(fmt.Sprintf("cp - %s %s", src, dest))
return copyFile(src, dest)
}

// copyDir will recursively copy a directory to dest
func copyDir(src, dest string) error {
func copyDir(src, dest string, ignorePaths []string) error {
info, err := os.Stat(src)
if err != nil {
return fmt.Errorf("error reading dest stats: %s", err.Error())
Expand All @@ -43,9 +55,10 @@ func copyDir(src, dest string) error {
}

for _, info := range infos {
if err := CopyFiles(
if err := CopyFilesWithIgnorePaths(
filepath.Join(src, info.Name()),
filepath.Join(dest, info.Name()),
ignorePaths,
); err != nil {
return err
}
Expand Down
64 changes: 55 additions & 9 deletions builder/copy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func Test_CopyFiles(t *testing.T) {
t.Fatalf("Unexpected copy error\n%v", err)
}

err = checkDestinationFiles(destDir, 2, mode)
err = checkDestinationFiles(destDir, 2, mode, nil)
if err != nil {
t.Fatalf("Destination file mode differs from source file mode\n%v", err)
}
Expand Down Expand Up @@ -71,12 +71,47 @@ func Test_CopyFiles_ToDestinationWithIntermediateFolder(t *testing.T) {
t.Fatalf("Unexpected copy error\n%v", err)
}

err = checkDestinationFiles(destDir+"/intermediate/", 1, mode)
err = checkDestinationFiles(destDir+"/intermediate/", 1, mode, nil)
if err != nil {
t.Fatalf("Destination file mode differs from source file mode\n%v", err)
}
}

func Test_CopyFilesWithIgnorePaths(t *testing.T) {
fileModes := []int{0600, 0640, 0644, 0700, 0755}

dir := os.TempDir()
for _, mode := range fileModes {
// set up a source folder with 2 file
srcDir, srcDirErr := setupSourceFolder(2, mode)
if srcDirErr != nil {
log.Fatal("Error creating source folder")
}
defer os.RemoveAll(srcDir)

// create a destination folder to copy the files to
destDir, destDirErr := ioutil.TempDir(dir, "openfaas-test-destination-")
if destDirErr != nil {
t.Fatalf("Error creating destination folder\n%v", destDirErr)
}
defer os.RemoveAll(destDir)

ignorePaths := []string{
fmt.Sprintf("%s/test-file-2", destDir),
}

err := CopyFilesWithIgnorePaths(srcDir, destDir+"/", ignorePaths)
if err != nil {
t.Fatalf("Unexpected copy error\n%v", err)
}

err = checkDestinationFiles(destDir, 2, mode, ignorePaths)
if err != nil {
t.Fatalf("Destination file mode differs from source file mode\n%v", err)
}
}
}

func setupSourceFolder(numberOfFiles, mode int) (string, error) {
dir := os.TempDir()
data := []byte("open faas")
Expand All @@ -99,15 +134,26 @@ func setupSourceFolder(numberOfFiles, mode int) (string, error) {
return srcDir, nil
}

func checkDestinationFiles(dir string, numberOfFiles, mode int) error {
func checkDestinationFiles(dir string, numberOfFiles, mode int, ignorePaths []string) error {
// Check each file inside the destination folder
for i := 1; i <= numberOfFiles; i++ {
fileStat, err := os.Stat(fmt.Sprintf("%s/test-file-%d", dir, i))
if os.IsNotExist(err) {
return err
}
if fileStat.Mode() != os.FileMode(mode) {
return errors.New("expected mode did not match")
filePath := fmt.Sprintf("%s/test-file-%d", dir, i)
fileStat, err := os.Stat(filePath)

for _, ignorePath := range ignorePaths {
if filePath == ignorePath {
if !os.IsNotExist(err) {
return err
}
} else {
if os.IsNotExist(err) {
return err
}

if fileStat.Mode() != os.FileMode(mode) {
return errors.New("expected mode did not match")
}
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions builder/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
// PublishImage will publish images as multi-arch
// TODO: refactor signature to a struct to simplify the length of the method header
func PublishImage(image string, handler string, functionName string, language string, nocache bool, squash bool, shrinkwrap bool, buildArgMap map[string]string,
buildOptions []string, tagMode schema.BuildFormat, buildLabelMap map[string]string, quietBuild bool, copyExtraPaths []string, platforms string, extraTags []string) error {
buildOptions []string, tagMode schema.BuildFormat, buildLabelMap map[string]string, quietBuild bool, copyExtraPaths []string, ignorePaths []string, platforms string, extraTags []string) error {

if stack.IsValidTemplate(language) {
pathToTemplateYAML := fmt.Sprintf("./template/%s/template.yml", language)
Expand All @@ -40,7 +40,7 @@ func PublishImage(image string, handler string, functionName string, language st
return fmt.Errorf("building %s, %s is an invalid path", imageName, handler)
}

tempPath, buildErr := createBuildContext(functionName, handler, language, isLanguageTemplate(language), langTemplate.HandlerFolder, copyExtraPaths)
tempPath, buildErr := createBuildContext(functionName, handler, language, isLanguageTemplate(language), langTemplate.HandlerFolder, copyExtraPaths, ignorePaths)
fmt.Printf("Building: %s with %s template. Please wait..\n", imageName, language)
if buildErr != nil {
return buildErr
Expand Down
2 changes: 2 additions & 0 deletions commands/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ func runBuild(cmd *cobra.Command, args []string) error {
buildLabelMap,
quietBuild,
copyExtra,
nil,
)
if err != nil {
return err
Expand Down Expand Up @@ -255,6 +256,7 @@ func build(services *stack.Services, queueDepth int, shrinkwrap, quietBuild bool
buildLabelMap,
quietBuild,
combinedExtraPaths,
function.IgnorePaths,
)

if err != nil {
Expand Down
9 changes: 5 additions & 4 deletions commands/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,12 @@ var publishCmd = &cobra.Command{
Short: "Builds and pushes multi-arch OpenFaaS container images",
Long: `Builds and pushes multi-arch OpenFaaS container images using Docker buildx.
Most users will want faas-cli build or faas-cli up for development and testing.
This command is designed to make releasing and publishing multi-arch container
This command is designed to make releasing and publishing multi-arch container
images easier.

A stack.yaml file is required, and any images that are built will not be
available in the local Docker library. This is due to technical constraints in
Docker and buildx. You must use a multi-arch template to use this command with
A stack.yaml file is required, and any images that are built will not be
available in the local Docker library. This is due to technical constraints in
Docker and buildx. You must use a multi-arch template to use this command with
correctly configured TARGETPLATFORM and BUILDPLATFORM arguments.

See also: faas-cli build`,
Expand Down Expand Up @@ -205,6 +205,7 @@ func publish(services *stack.Services, queueDepth int, shrinkwrap, quietBuild bo
buildLabelMap,
quietBuild,
combinedExtraPaths,
function.IgnorePaths,
platforms,
extraTags,
)
Expand Down
4 changes: 2 additions & 2 deletions commands/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func init() {
// versionCmd displays version information
var versionCmd = &cobra.Command{
Use: "version [--short-version] [--gateway GATEWAY_URL]",
Short: "Display the clients version information",
Short: "Display the clients version information. Foo",
Long: fmt.Sprintf(`The version command returns the current clients version information.

This currently consists of the GitSHA from which the client was built.
Expand Down Expand Up @@ -115,7 +115,7 @@ func printServerVersions() error {
Provider
name: %s
orchestration: %s
version: %s
version: %s
sha: %s
`, gatewayInfo.Provider.Name, gatewayInfo.Provider.Orchestration, gatewayInfo.Provider.Version.Release, gatewayInfo.Provider.Version.SHA)
return nil
Expand Down
Empty file.
2 changes: 2 additions & 0 deletions stack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ functions:
lang: node
handler: ./sample/nodejs-echo
image: alexellis/faas-nodejs-echo:0.1
ignore_paths:
- node_modules
# limits:
# memory: 40m
# requests:
Expand Down
4 changes: 4 additions & 0 deletions stack/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ type Function struct {
// BuildArgs for providing build-args
BuildArgs map[string]string `yaml:"build_args,omitempty"`

// IgnorePaths are relative files or directories that will not be copied to the
// build folder e.g. node_modules
IgnorePaths []string `yaml:"ignore_paths,omitempty"`

// Platforms for use with buildx and faas-cli publish
Platforms string `yaml:"platforms,omitempty"`
}
Expand Down