github.com/ablease/cli@v6.37.1-0.20180613014814-3adbb7d7fb19+incompatible/actor/pushaction/merge_and_validate_settings_and_manifest.go (about)

     1  package pushaction
     2  
     3  import (
     4  	"net/url"
     5  	"os"
     6  	"path/filepath"
     7  
     8  	"code.cloudfoundry.org/cli/actor/actionerror"
     9  	"code.cloudfoundry.org/cli/util/manifest"
    10  	log "github.com/sirupsen/logrus"
    11  )
    12  
    13  func (actor Actor) MergeAndValidateSettingsAndManifests(cmdLineSettings CommandLineSettings, apps []manifest.Application) ([]manifest.Application, error) {
    14  	var mergedApps []manifest.Application
    15  
    16  	if len(apps) == 0 {
    17  		log.Info("no manifest, generating one from command line settings")
    18  		mergedApps = append(mergedApps, cmdLineSettings.OverrideManifestSettings(manifest.Application{}))
    19  	} else {
    20  		if cmdLineSettings.Name != "" && len(apps) > 1 {
    21  			var err error
    22  			apps, err = actor.selectApp(cmdLineSettings.Name, apps)
    23  			if err != nil {
    24  				return nil, err
    25  			}
    26  		}
    27  		err := actor.validateCommandLineSettingsAndManifestCombinations(cmdLineSettings, apps)
    28  		if err != nil {
    29  			return nil, err
    30  		}
    31  
    32  		for _, app := range apps {
    33  			mergedApps = append(mergedApps, cmdLineSettings.OverrideManifestSettings(app))
    34  		}
    35  	}
    36  
    37  	mergedApps, err := actor.sanitizeAppPath(mergedApps)
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  	mergedApps = actor.setSaneEndpoint(mergedApps)
    42  
    43  	log.Debugf("merged app settings: %#v", mergedApps)
    44  
    45  	err = actor.validateMergedSettings(mergedApps)
    46  	if err != nil {
    47  		log.Errorln("validation error post merge:", err)
    48  		return nil, err
    49  	}
    50  	return mergedApps, nil
    51  }
    52  
    53  func (Actor) selectApp(appName string, apps []manifest.Application) ([]manifest.Application, error) {
    54  	var returnedApps []manifest.Application
    55  	for _, app := range apps {
    56  		if app.Name == appName {
    57  			returnedApps = append(returnedApps, app)
    58  		}
    59  	}
    60  	if len(returnedApps) == 0 {
    61  		return nil, actionerror.AppNotFoundInManifestError{Name: appName}
    62  	}
    63  
    64  	return returnedApps, nil
    65  }
    66  
    67  func (Actor) setSaneEndpoint(apps []manifest.Application) []manifest.Application {
    68  	for i, app := range apps {
    69  		if app.HealthCheckType == "http" && app.HealthCheckHTTPEndpoint == "" {
    70  			apps[i].HealthCheckHTTPEndpoint = "/"
    71  		}
    72  	}
    73  
    74  	return apps
    75  }
    76  
    77  func (Actor) sanitizeAppPath(apps []manifest.Application) ([]manifest.Application, error) {
    78  	for i, app := range apps {
    79  		if app.Path != "" {
    80  			var err error
    81  			apps[i].Path, err = filepath.Abs(app.Path)
    82  			if err != nil {
    83  				return nil, err
    84  			}
    85  		}
    86  	}
    87  
    88  	return apps, nil
    89  }
    90  
    91  func (Actor) validateCommandLineSettingsAndManifestCombinations(cmdLineSettings CommandLineSettings, apps []manifest.Application) error {
    92  	if len(apps) > 1 {
    93  		switch {
    94  		case
    95  			cmdLineSettings.Buildpacks != nil,
    96  			cmdLineSettings.Command.IsSet,
    97  			cmdLineSettings.DefaultRouteDomain != "",
    98  			cmdLineSettings.DefaultRouteHostname != "",
    99  			cmdLineSettings.DiskQuota != 0,
   100  			cmdLineSettings.DockerImage != "",
   101  			cmdLineSettings.DockerUsername != "",
   102  			cmdLineSettings.DropletPath != "",
   103  			cmdLineSettings.HealthCheckTimeout != 0,
   104  			cmdLineSettings.HealthCheckType != "",
   105  			cmdLineSettings.Instances.IsSet,
   106  			cmdLineSettings.Memory != 0,
   107  			cmdLineSettings.NoHostname,
   108  			cmdLineSettings.NoRoute,
   109  			cmdLineSettings.ProvidedAppPath != "",
   110  			cmdLineSettings.RandomRoute,
   111  			cmdLineSettings.RoutePath != "",
   112  			cmdLineSettings.StackName != "":
   113  			log.Error("cannot use some parameters with multiple apps")
   114  			return actionerror.CommandLineOptionsWithMultipleAppsError{}
   115  		}
   116  	}
   117  
   118  	for _, app := range apps {
   119  		switch {
   120  		case app.NoRoute && len(app.Routes) > 0:
   121  			return actionerror.PropertyCombinationError{AppName: app.Name, Properties: []string{"no-route", "routes"}}
   122  		case app.DeprecatedDomain != nil ||
   123  			app.DeprecatedDomains != nil ||
   124  			app.DeprecatedHost != nil ||
   125  			app.DeprecatedHosts != nil ||
   126  			app.DeprecatedNoHostname != nil:
   127  
   128  			deprecatedFields := []string{}
   129  			if app.DeprecatedDomain != nil {
   130  				deprecatedFields = append(deprecatedFields, "domain")
   131  			}
   132  			if app.DeprecatedDomains != nil {
   133  				deprecatedFields = append(deprecatedFields, "domains")
   134  			}
   135  			if app.DeprecatedHost != nil {
   136  				deprecatedFields = append(deprecatedFields, "host")
   137  			}
   138  			if app.DeprecatedHosts != nil {
   139  				deprecatedFields = append(deprecatedFields, "hosts")
   140  			}
   141  			if app.DeprecatedNoHostname != nil {
   142  				deprecatedFields = append(deprecatedFields, "no-hostname")
   143  			}
   144  			return actionerror.TriggerLegacyPushError{DomainHostRelated: deprecatedFields}
   145  		case len(app.Routes) > 0:
   146  			commandLineOptionsAndManifestConflictErr := actionerror.CommandLineOptionsAndManifestConflictError{
   147  				ManifestAttribute:  "route",
   148  				CommandLineOptions: []string{"-d", "--hostname", "-n", "--no-hostname", "--route-path"},
   149  			}
   150  			if cmdLineSettings.DefaultRouteDomain != "" ||
   151  				cmdLineSettings.DefaultRouteHostname != "" ||
   152  				cmdLineSettings.NoHostname != false ||
   153  				cmdLineSettings.RoutePath != "" {
   154  				return commandLineOptionsAndManifestConflictErr
   155  			}
   156  		}
   157  	}
   158  
   159  	return nil
   160  }
   161  
   162  func (actor Actor) validateMergedSettings(apps []manifest.Application) error {
   163  	for i, app := range apps {
   164  		log.WithField("index", i).Info("validating app")
   165  		if app.Name == "" {
   166  			log.WithField("index", i).Error("does not contain an app name")
   167  			return actionerror.MissingNameError{}
   168  		}
   169  
   170  		for _, route := range app.Routes {
   171  			err := actor.validateRoute(route)
   172  			if err != nil {
   173  				return err
   174  			}
   175  		}
   176  
   177  		if app.DockerImage != "" {
   178  			if app.DockerUsername != "" && app.DockerPassword == "" {
   179  				log.WithField("app", app.Name).Error("no docker password found")
   180  				return actionerror.DockerPasswordNotSetError{}
   181  			}
   182  			if app.Buildpack.IsSet {
   183  				return actionerror.PropertyCombinationError{AppName: app.Name, Properties: []string{"docker", "buildpack"}}
   184  			}
   185  			if app.Buildpacks != nil {
   186  				return actionerror.PropertyCombinationError{AppName: app.Name, Properties: []string{"docker", "buildpacks"}}
   187  			}
   188  			if app.Path != "" {
   189  				return actionerror.PropertyCombinationError{AppName: app.Name, Properties: []string{"docker", "path"}}
   190  			}
   191  			if app.DropletPath != "" {
   192  				return actionerror.PropertyCombinationError{AppName: app.Name, Properties: []string{"docker", "droplet"}}
   193  			}
   194  		}
   195  
   196  		if app.DropletPath != "" {
   197  			if app.Path != "" {
   198  				return actionerror.PropertyCombinationError{AppName: app.Name, Properties: []string{"droplet", "path"}}
   199  			}
   200  			if app.Buildpack.IsSet {
   201  				return actionerror.PropertyCombinationError{AppName: app.Name, Properties: []string{"droplet", "buildpack"}}
   202  			}
   203  			if app.Buildpacks != nil {
   204  				return actionerror.PropertyCombinationError{AppName: app.Name, Properties: []string{"droplet", "buildpacks"}}
   205  			}
   206  		}
   207  
   208  		if app.DockerImage == "" && app.DropletPath == "" {
   209  			_, err := os.Stat(app.Path)
   210  			if os.IsNotExist(err) {
   211  				log.WithField("path", app.Path).Error("app path does not exist")
   212  				return actionerror.NonexistentAppPathError{Path: app.Path}
   213  			}
   214  		}
   215  
   216  		if app.NoRoute {
   217  			if app.Hostname != "" {
   218  				return actionerror.PropertyCombinationError{AppName: app.Name, Properties: []string{"hostname", "no-route"}}
   219  			}
   220  			if app.NoHostname {
   221  				return actionerror.PropertyCombinationError{AppName: app.Name, Properties: []string{"no-hostname", "no-route"}}
   222  			}
   223  			if app.RoutePath != "" {
   224  				return actionerror.PropertyCombinationError{AppName: app.Name, Properties: []string{"route-path", "no-route"}}
   225  			}
   226  		}
   227  
   228  		if app.HealthCheckHTTPEndpoint != "" && app.HealthCheckType != "http" {
   229  			return actionerror.HTTPHealthCheckInvalidError{}
   230  		}
   231  
   232  		if app.Buildpacks != nil && app.Buildpack.IsSet {
   233  			return actionerror.PropertyCombinationError{AppName: app.Name, Properties: []string{"buildpack", "buildpacks"}}
   234  		}
   235  
   236  		if len(app.Buildpacks) > 1 {
   237  			for _, b := range app.Buildpacks {
   238  				if b == "null" || b == "default" {
   239  					return actionerror.InvalidBuildpacksError{}
   240  				}
   241  			}
   242  		}
   243  	}
   244  	return nil
   245  }
   246  
   247  func (actor Actor) validateRoute(route string) error {
   248  	_, err := url.Parse(route)
   249  	if err != nil || !actor.urlValidator.MatchString(route) {
   250  		return actionerror.InvalidRouteError{Route: route}
   251  	}
   252  
   253  	return nil
   254  }