github.com/loggregator/cli@v6.33.1-0.20180224010324-82334f081791+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(settings 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, settings.OverrideManifestSettings(manifest.Application{})) 19 } else { 20 if settings.Name != "" && len(apps) > 1 { 21 var err error 22 apps, err = actor.selectApp(settings.Name, apps) 23 if err != nil { 24 return nil, err 25 } 26 } 27 28 err := actor.validatePremergedSettings(settings, apps) 29 if err != nil { 30 return nil, err 31 } 32 33 for _, app := range apps { 34 mergedApps = append(mergedApps, settings.OverrideManifestSettings(app)) 35 } 36 } 37 38 mergedApps, err := actor.sanitizeAppPath(mergedApps) 39 if err != nil { 40 return nil, err 41 } 42 mergedApps = actor.setSaneEndpoint(mergedApps) 43 44 log.Debugf("merged app settings: %#v", mergedApps) 45 46 err = actor.validateMergedSettings(mergedApps) 47 if err != nil { 48 log.Errorln("validation error post merge:", err) 49 return nil, err 50 } 51 return mergedApps, nil 52 } 53 54 func (Actor) selectApp(appName string, apps []manifest.Application) ([]manifest.Application, error) { 55 var returnedApps []manifest.Application 56 for _, app := range apps { 57 if app.Name == appName { 58 returnedApps = append(returnedApps, app) 59 } 60 } 61 if len(returnedApps) == 0 { 62 return nil, actionerror.AppNotFoundInManifestError{Name: appName} 63 } 64 65 return returnedApps, nil 66 } 67 68 func (Actor) setSaneEndpoint(apps []manifest.Application) []manifest.Application { 69 for i, app := range apps { 70 if app.HealthCheckType == "http" && app.HealthCheckHTTPEndpoint == "" { 71 apps[i].HealthCheckHTTPEndpoint = "/" 72 } 73 } 74 75 return apps 76 } 77 78 func (Actor) sanitizeAppPath(apps []manifest.Application) ([]manifest.Application, error) { 79 for i, app := range apps { 80 if app.Path != "" { 81 var err error 82 apps[i].Path, err = filepath.Abs(app.Path) 83 if err != nil { 84 return nil, err 85 } 86 } 87 } 88 89 return apps, nil 90 } 91 92 func (Actor) validatePremergedSettings(settings CommandLineSettings, apps []manifest.Application) error { 93 if len(apps) > 1 { 94 switch { 95 case 96 settings.Buildpack.IsSet, 97 settings.Command.IsSet, 98 settings.DefaultRouteDomain != "", 99 settings.DefaultRouteHostname != "", 100 settings.DiskQuota != 0, 101 settings.DockerImage != "", 102 settings.DockerUsername != "", 103 settings.HealthCheckTimeout != 0, 104 settings.HealthCheckType != "", 105 settings.Instances.IsSet, 106 settings.Memory != 0, 107 settings.NoHostname, 108 settings.NoRoute, 109 settings.ProvidedAppPath != "", 110 settings.RandomRoute, 111 settings.RoutePath != "", 112 settings.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 || app.DeprecatedDomains != nil || app.DeprecatedHost != nil || app.DeprecatedHosts != nil || app.DeprecatedNoHostname != nil: 123 deprecatedFields := []string{} 124 if app.DeprecatedDomain != nil { 125 deprecatedFields = append(deprecatedFields, "domain") 126 } 127 if app.DeprecatedDomains != nil { 128 deprecatedFields = append(deprecatedFields, "domains") 129 } 130 if app.DeprecatedHost != nil { 131 deprecatedFields = append(deprecatedFields, "host") 132 } 133 if app.DeprecatedHosts != nil { 134 deprecatedFields = append(deprecatedFields, "hosts") 135 } 136 if app.DeprecatedNoHostname != nil { 137 deprecatedFields = append(deprecatedFields, "no-hostname") 138 } 139 return actionerror.TriggerLegacyPushError{DomainHostRelated: deprecatedFields} 140 case len(app.Routes) > 0: 141 commandLineOptionsAndManifestConflictErr := actionerror.CommandLineOptionsAndManifestConflictError{ 142 ManifestAttribute: "route", 143 CommandLineOptions: []string{"-d", "--hostname", "-n", "--no-hostname", "--route-path"}, 144 } 145 if settings.DefaultRouteDomain != "" || 146 settings.DefaultRouteHostname != "" || 147 settings.NoHostname != false || 148 settings.RoutePath != "" { 149 return commandLineOptionsAndManifestConflictErr 150 } 151 } 152 } 153 154 return nil 155 } 156 157 func (actor Actor) validateMergedSettings(apps []manifest.Application) error { 158 for i, app := range apps { 159 log.WithField("index", i).Info("validating app") 160 if app.Name == "" { 161 log.WithField("index", i).Error("does not contain an app name") 162 return actionerror.MissingNameError{} 163 } 164 165 for _, route := range app.Routes { 166 err := actor.validateRoute(route) 167 if err != nil { 168 return err 169 } 170 } 171 172 if app.DockerImage == "" { 173 _, err := os.Stat(app.Path) 174 if os.IsNotExist(err) { 175 log.WithField("path", app.Path).Error("app path does not exist") 176 return actionerror.NonexistentAppPathError{Path: app.Path} 177 } 178 } else { 179 if app.DockerUsername != "" && app.DockerPassword == "" { 180 log.WithField("app", app.Name).Error("no docker password found") 181 return actionerror.DockerPasswordNotSetError{} 182 } 183 if app.Buildpack.IsSet { 184 return actionerror.PropertyCombinationError{AppName: app.Name, Properties: []string{"docker", "buildpack"}} 185 } 186 if app.Path != "" { 187 return actionerror.PropertyCombinationError{AppName: app.Name, Properties: []string{"docker", "path"}} 188 } 189 } 190 191 if app.NoRoute { 192 if app.Hostname != "" { 193 return actionerror.PropertyCombinationError{AppName: app.Name, Properties: []string{"hostname", "no-route"}} 194 } 195 if app.NoHostname { 196 return actionerror.PropertyCombinationError{AppName: app.Name, Properties: []string{"no-hostname", "no-route"}} 197 } 198 if app.RoutePath != "" { 199 return actionerror.PropertyCombinationError{AppName: app.Name, Properties: []string{"route-path", "no-route"}} 200 } 201 } 202 203 if app.HealthCheckHTTPEndpoint != "" && app.HealthCheckType != "http" { 204 return actionerror.HTTPHealthCheckInvalidError{} 205 } 206 } 207 return nil 208 } 209 210 func (actor Actor) validateRoute(route string) error { 211 _, err := url.Parse(route) 212 if err != nil || !actor.urlValidator.MatchString(route) { 213 return actionerror.InvalidRouteError{Route: route} 214 } 215 216 return nil 217 }