expectedAction := models.Codependent( &models.RunAction{ User: "******", Path: "/tmp/lifecycle/launcher", Args: []string{ "app", "the-start-command with-arguments", "the-execution-metadata", }, Env: []*models.EnvironmentVariable{ {Name: "foo", Value: "bar"}, {Name: "PORT", Value: "8080"}, }, ResourceLimits: &models.ResourceLimits{ Nofile: &expectedNumFiles, }, LogSource: "APP", }, &models.RunAction{ User: "******", Path: "/tmp/lifecycle/diego-sshd", Args: []string{ "-address=0.0.0.0:2222", "-hostKey=pem-host-private-key", "-authorizedKey=authorized-user-key", "-inheritDaemonEnv", "-logLevel=fatal", }, Env: []*models.EnvironmentVariable{ {Name: "foo", Value: "bar"}, {Name: "PORT", Value: "8080"}, }, ResourceLimits: &models.ResourceLimits{ Nofile: &expectedNumFiles, }, }, )
}, { "run": { "resource_limits": {}, "path": "echo", "user": "******", "suppress_log_output": false } } ] }`, models.WrapAction(models.Codependent( &models.DownloadAction{ From: "web_location", To: "local_location", CacheKey: "elephant", User: "******", }, &models.RunAction{Path: "echo", User: "******", ResourceLimits: &models.ResourceLimits{}}, )), ) Describe("Validate", func() { var codependentAction *models.CodependentAction Context("when the action has 'actions' as a slice of valid actions", func() { It("is valid", func() { codependentAction = models.Codependent( &models.UploadAction{ From: "local_location", To: "web_location",
func (b *BuildpackRecipeBuilder) Build(desiredApp *cc_messages.DesireAppRequestFromCC) (*models.DesiredLRP, error) { lrpGuid := desiredApp.ProcessGuid buildLogger := b.logger.Session("message-builder") if desiredApp.DropletUri == "" { buildLogger.Error("desired-app-invalid", ErrDropletSourceMissing, lager.Data{"desired-app": desiredApp}) return nil, ErrDropletSourceMissing } if desiredApp.DropletUri != "" && desiredApp.DockerImageUrl != "" { buildLogger.Error("desired-app-invalid", ErrMultipleAppSources, lager.Data{"desired-app": desiredApp}) return nil, ErrMultipleAppSources } var lifecycle = "buildpack/" + desiredApp.Stack lifecyclePath, ok := b.config.Lifecycles[lifecycle] if !ok { buildLogger.Error("unknown-lifecycle", ErrNoLifecycleDefined, lager.Data{ "lifecycle": lifecycle, }) return nil, ErrNoLifecycleDefined } lifecycleURL := lifecycleDownloadURL(lifecyclePath, b.config.FileServerURL) rootFSPath := models.PreloadedRootFS(desiredApp.Stack) var containerEnvVars []*models.EnvironmentVariable containerEnvVars = append(containerEnvVars, &models.EnvironmentVariable{"LANG", DefaultLANG}) numFiles := DefaultFileDescriptorLimit if desiredApp.FileDescriptors != 0 { numFiles = desiredApp.FileDescriptors } var setup []models.ActionInterface var actions []models.ActionInterface var monitor models.ActionInterface cachedDependencies := []*models.CachedDependency{} cachedDependencies = append(cachedDependencies, &models.CachedDependency{ From: lifecycleURL, To: "/tmp/lifecycle", CacheKey: fmt.Sprintf("%s-lifecycle", strings.Replace(lifecycle, "/", "-", 1)), }) desiredAppPorts, err := b.ExtractExposedPorts(desiredApp) if err != nil { return nil, err } switch desiredApp.HealthCheckType { case cc_messages.PortHealthCheckType, cc_messages.UnspecifiedHealthCheckType: monitor = models.Timeout(getParallelAction(desiredAppPorts, "vcap"), 30*time.Second) } setup = append(setup, &models.DownloadAction{ From: desiredApp.DropletUri, To: ".", CacheKey: fmt.Sprintf("droplets-%s", lrpGuid), User: "******", }) actions = append(actions, &models.RunAction{ User: "******", Path: "/tmp/lifecycle/launcher", Args: append( []string{"app"}, desiredApp.StartCommand, desiredApp.ExecutionMetadata, ), Env: createLrpEnv(desiredApp.Environment, desiredAppPorts[0]), LogSource: AppLogSource, ResourceLimits: &models.ResourceLimits{ Nofile: &numFiles, }, }) desiredAppRoutingInfo, err := helpers.CCRouteInfoToRoutes(desiredApp.RoutingInfo, desiredAppPorts) if err != nil { buildLogger.Error("marshaling-cc-route-info-failed", err) return nil, err } if desiredApp.AllowSSH { hostKeyPair, err := b.config.KeyFactory.NewKeyPair(1024) if err != nil { buildLogger.Error("new-host-key-pair-failed", err) return nil, err } userKeyPair, err := b.config.KeyFactory.NewKeyPair(1024) if err != nil { buildLogger.Error("new-user-key-pair-failed", err) return nil, err } actions = append(actions, &models.RunAction{ User: "******", Path: "/tmp/lifecycle/diego-sshd", Args: []string{ "-address=" + fmt.Sprintf("0.0.0.0:%d", DefaultSSHPort), "-hostKey=" + hostKeyPair.PEMEncodedPrivateKey(), "-authorizedKey=" + userKeyPair.AuthorizedKey(), "-inheritDaemonEnv", "-logLevel=fatal", }, Env: createLrpEnv(desiredApp.Environment, desiredAppPorts[0]), ResourceLimits: &models.ResourceLimits{ Nofile: &numFiles, }, }) sshRoutePayload, err := json.Marshal(ssh_routes.SSHRoute{ ContainerPort: 2222, PrivateKey: userKeyPair.PEMEncodedPrivateKey(), HostFingerprint: hostKeyPair.Fingerprint(), }) if err != nil { buildLogger.Error("marshaling-ssh-route-failed", err) return nil, err } sshRouteMessage := json.RawMessage(sshRoutePayload) desiredAppRoutingInfo[ssh_routes.DIEGO_SSH] = &sshRouteMessage desiredAppPorts = append(desiredAppPorts, DefaultSSHPort) } setupAction := models.Serial(setup...) actionAction := models.Codependent(actions...) return &models.DesiredLRP{ Privileged: true, Domain: cc_messages.AppLRPDomain, ProcessGuid: lrpGuid, Instances: int32(desiredApp.NumInstances), Routes: &desiredAppRoutingInfo, Annotation: desiredApp.ETag, CpuWeight: cpuWeight(desiredApp.MemoryMB), MemoryMb: int32(desiredApp.MemoryMB), DiskMb: int32(desiredApp.DiskMB), Ports: desiredAppPorts, RootFs: rootFSPath, LogGuid: desiredApp.LogGuid, LogSource: LRPLogSource, MetricsGuid: desiredApp.LogGuid, EnvironmentVariables: containerEnvVars, CachedDependencies: cachedDependencies, Setup: models.WrapAction(setupAction), Action: models.WrapAction(actionAction), Monitor: models.WrapAction(monitor), StartTimeout: uint32(desiredApp.HealthCheckTimeoutInSeconds), EgressRules: desiredApp.EgressRules, LegacyDownloadUser: "******", }, nil }
Eventually(desiredLRPsWithoutModificationTag).Should(ContainElement(&models.DesiredLRP{ ProcessGuid: "process-guid-1", Domain: "cf-apps", Instances: 42, RootFs: models.PreloadedRootFS("some-stack"), Setup: models.WrapAction(expectedSetupActions1), StartTimeout: 123456, EnvironmentVariables: []*models.EnvironmentVariable{ {Name: "LANG", Value: recipebuilder.DefaultLANG}, }, Action: models.WrapAction(models.Codependent(&models.RunAction{ User: "******", Path: "/tmp/lifecycle/launcher", Args: []string{"app", "start-command-1", "execution-metadata-1"}, Env: []*models.EnvironmentVariable{ {Name: "env-key-1", Value: "env-value-1"}, {Name: "env-key-2", Value: "env-value-2"}, {Name: "PORT", Value: "8080"}, }, ResourceLimits: &models.ResourceLimits{Nofile: &nofile}, LogSource: recipebuilder.AppLogSource, })), Monitor: models.WrapAction(models.Timeout( &models.ParallelAction{ Actions: []*models.Action{ &models.Action{ RunAction: &models.RunAction{ User: "******", Path: "/tmp/lifecycle/healthcheck", Args: []string{"-port=8080"}, LogSource: "HEALTH", ResourceLimits: &models.ResourceLimits{
} }, { "run": { "resource_limits": {}, "path": "echo", "user": "******" } } ] }`, models.Codependent( &models.DownloadAction{ From: "web_location", To: "local_location", CacheKey: "elephant", User: "******", }, &models.RunAction{Path: "echo", User: "******", ResourceLimits: &models.ResourceLimits{}}, ), ) itSerializesAndDeserializes( `{}`, models.WrapAction(&models.CodependentAction{}), ) itSerializesAndDeserializes( `{ "actions": [null] }`,