github.com/elopio/cli@v6.21.2-0.20160902224010-ea909d1fdb2f+incompatible/cf/actors/push.go (about)

     1  package actors
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  	"runtime"
     9  
    10  	"code.cloudfoundry.org/cli/cf/api/applicationbits"
    11  	"code.cloudfoundry.org/cli/cf/api/resources"
    12  	"code.cloudfoundry.org/cli/cf/appfiles"
    13  	. "code.cloudfoundry.org/cli/cf/i18n"
    14  	"code.cloudfoundry.org/cli/cf/models"
    15  	"code.cloudfoundry.org/gofileutils/fileutils"
    16  )
    17  
    18  const windowsPathPrefix = `\\?\`
    19  
    20  //go:generate counterfeiter . PushActor
    21  
    22  type PushActor interface {
    23  	UploadApp(appGUID string, zipFile *os.File, presentFiles []resources.AppFileResource) error
    24  	ProcessPath(dirOrZipFile string, f func(string) error) error
    25  	GatherFiles(localFiles []models.AppFileFields, appDir string, uploadDir string) ([]resources.AppFileResource, bool, error)
    26  	ValidateAppParams(apps []models.AppParams) []error
    27  	MapManifestRoute(routeName string, app models.Application, appParamsFromContext models.AppParams) error
    28  }
    29  
    30  type PushActorImpl struct {
    31  	appBitsRepo applicationbits.Repository
    32  	appfiles    appfiles.AppFiles
    33  	zipper      appfiles.Zipper
    34  	routeActor  RouteActor
    35  }
    36  
    37  func NewPushActor(appBitsRepo applicationbits.Repository, zipper appfiles.Zipper, appfiles appfiles.AppFiles, routeActor RouteActor) PushActor {
    38  	return PushActorImpl{
    39  		appBitsRepo: appBitsRepo,
    40  		appfiles:    appfiles,
    41  		zipper:      zipper,
    42  		routeActor:  routeActor,
    43  	}
    44  }
    45  
    46  // ProcessPath takes in a director of app files or a zip file which contains
    47  // the app files. If given a zip file, it will extract the zip to a temporary
    48  // location, call the provided callback with that location, and then clean up
    49  // the location after the callback has been executed.
    50  //
    51  // This was done so that the caller of ProcessPath wouldn't need to know if it
    52  // was a zip file or an app dir that it was given, and the caller would not be
    53  // responsible for cleaning up the temporary directory ProcessPath creates when
    54  // given a zip.
    55  func (actor PushActorImpl) ProcessPath(dirOrZipFile string, f func(string) error) error {
    56  	if !actor.zipper.IsZipFile(dirOrZipFile) {
    57  		if filepath.IsAbs(dirOrZipFile) {
    58  			appDir, err := filepath.EvalSymlinks(dirOrZipFile)
    59  			if err != nil {
    60  				return err
    61  			}
    62  			err = f(appDir)
    63  			if err != nil {
    64  				return err
    65  			}
    66  		} else {
    67  			absPath, err := filepath.Abs(dirOrZipFile)
    68  			if err != nil {
    69  				return err
    70  			}
    71  			appDir, err := filepath.EvalSymlinks(absPath)
    72  			if err != nil {
    73  				return err
    74  			}
    75  
    76  			err = f(appDir)
    77  			if err != nil {
    78  				return err
    79  			}
    80  		}
    81  
    82  		return nil
    83  	}
    84  
    85  	tempDir, err := ioutil.TempDir("", "unzipped-app")
    86  	if err != nil {
    87  		return err
    88  	}
    89  
    90  	err = actor.zipper.Unzip(dirOrZipFile, tempDir)
    91  	if err != nil {
    92  		return err
    93  	}
    94  
    95  	err = f(tempDir)
    96  	if err != nil {
    97  		return err
    98  	}
    99  
   100  	err = os.RemoveAll(tempDir)
   101  	if err != nil {
   102  		return err
   103  	}
   104  
   105  	return nil
   106  }
   107  
   108  func (actor PushActorImpl) GatherFiles(localFiles []models.AppFileFields, appDir string, uploadDir string) ([]resources.AppFileResource, bool, error) {
   109  	appFileResource := []resources.AppFileResource{}
   110  	for _, file := range localFiles {
   111  		appFileResource = append(appFileResource, resources.AppFileResource{
   112  			Path: file.Path,
   113  			Sha1: file.Sha1,
   114  			Size: file.Size,
   115  		})
   116  	}
   117  
   118  	remoteFiles, err := actor.appBitsRepo.GetApplicationFiles(appFileResource)
   119  	if err != nil {
   120  		return []resources.AppFileResource{}, false, err
   121  	}
   122  
   123  	filesToUpload := make([]models.AppFileFields, len(localFiles), len(localFiles))
   124  	copy(filesToUpload, localFiles)
   125  
   126  	for _, remoteFile := range remoteFiles {
   127  		for i, fileToUpload := range filesToUpload {
   128  			if remoteFile.Path == fileToUpload.Path {
   129  				filesToUpload = append(filesToUpload[:i], filesToUpload[i+1:]...)
   130  			}
   131  		}
   132  	}
   133  
   134  	err = actor.appfiles.CopyFiles(filesToUpload, appDir, uploadDir)
   135  	if err != nil {
   136  		return []resources.AppFileResource{}, false, err
   137  	}
   138  
   139  	_, err = os.Stat(filepath.Join(appDir, ".cfignore"))
   140  	if err == nil {
   141  		err = fileutils.CopyPathToPath(filepath.Join(appDir, ".cfignore"), filepath.Join(uploadDir, ".cfignore"))
   142  		if err != nil {
   143  			return []resources.AppFileResource{}, false, err
   144  		}
   145  	}
   146  
   147  	for i := range remoteFiles {
   148  		fullPath, err := filepath.Abs(filepath.Join(appDir, remoteFiles[i].Path))
   149  		if err != nil {
   150  			return []resources.AppFileResource{}, false, err
   151  		}
   152  
   153  		if runtime.GOOS == "windows" {
   154  			fullPath = windowsPathPrefix + fullPath
   155  		}
   156  		fileInfo, err := os.Lstat(fullPath)
   157  		if err != nil {
   158  			return []resources.AppFileResource{}, false, err
   159  		}
   160  		fileMode := fileInfo.Mode()
   161  
   162  		if runtime.GOOS == "windows" {
   163  			fileMode = fileMode | 0700
   164  		}
   165  
   166  		remoteFiles[i].Mode = fmt.Sprintf("%#o", fileMode)
   167  	}
   168  
   169  	return remoteFiles, len(filesToUpload) > 0, nil
   170  }
   171  
   172  func (actor PushActorImpl) UploadApp(appGUID string, zipFile *os.File, presentFiles []resources.AppFileResource) error {
   173  	return actor.appBitsRepo.UploadBits(appGUID, zipFile, presentFiles)
   174  }
   175  
   176  func (actor PushActorImpl) ValidateAppParams(apps []models.AppParams) []error {
   177  	errs := []error{}
   178  
   179  	for _, app := range apps {
   180  		appName := app.Name
   181  
   182  		if app.Routes != nil {
   183  			if app.Hosts != nil {
   184  				errs = append(errs, fmt.Errorf(T("Application {{.AppName}} must not be configured with both 'routes' and 'host'/'hosts'", map[string]interface{}{"AppName": appName})))
   185  			}
   186  
   187  			if app.Domains != nil {
   188  				errs = append(errs, fmt.Errorf(T("Application {{.AppName}} must not be configured with both 'routes' and 'domain'/'domains'", map[string]interface{}{"AppName": appName})))
   189  			}
   190  
   191  			if app.NoHostname != nil {
   192  				errs = append(errs, fmt.Errorf(T("Application {{.AppName}} must not be configured with both 'routes' and 'no-hostname'", map[string]interface{}{"AppName": appName})))
   193  			}
   194  		}
   195  	}
   196  
   197  	if len(errs) > 0 {
   198  		return errs
   199  	}
   200  
   201  	return nil
   202  }
   203  
   204  func (actor PushActorImpl) MapManifestRoute(routeName string, app models.Application, appParamsFromContext models.AppParams) error {
   205  	return actor.routeActor.FindAndBindRoute(routeName, app, appParamsFromContext)
   206  }