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 }