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