})

		Context("when everything is correct", func() {
			It("does not error", func() {
				Expect(err).NotTo(HaveOccurred())
			})

			It("builds a valid DesiredLRP", func() {
				Expect(desiredLRP.ProcessGuid).To(Equal("the-app-guid-the-app-version"))
				Expect(desiredLRP.Instances).To(BeEquivalentTo(23))
				Expect(*desiredLRP.Routes).To(Equal(cfroutes.CFRoutes{
					{Hostnames: []string{"route1", "route2"}, Port: 8080},
				}.RoutingInfo()))

				Expect(desiredLRP.Annotation).To(Equal("etag-updated-at"))
				Expect(desiredLRP.RootFs).To(Equal(models.PreloadedRootFS("some-stack")))
				Expect(desiredLRP.MemoryMb).To(BeEquivalentTo(128))
				Expect(desiredLRP.DiskMb).To(BeEquivalentTo(512))
				Expect(desiredLRP.Ports).To(Equal([]uint32{8080}))
				Expect(desiredLRP.Privileged).To(BeTrue())
				Expect(desiredLRP.StartTimeout).To(BeEquivalentTo(123456))

				Expect(desiredLRP.LogGuid).To(Equal("the-log-id"))
				Expect(desiredLRP.LogSource).To(Equal("CELL"))

				Expect(desiredLRP.EnvironmentVariables).To(ConsistOf(&models.EnvironmentVariable{"LANG", recipebuilder.DefaultLANG}))

				Expect(desiredLRP.MetricsGuid).To(Equal("the-log-id"))

				expectedSetup := models.Serial(
					&models.DownloadAction{
Example #2
0
func (backend *dockerBackend) BuildRecipe(stagingGuid string, request cc_messages.StagingRequestFromCC) (*models.TaskDefinition, string, string, error) {
	logger := backend.logger.Session("build-recipe", lager.Data{"app-id": request.AppId, "staging-guid": stagingGuid})
	logger.Info("staging-request")

	var lifecycleData cc_messages.DockerStagingData
	err := json.Unmarshal(*request.LifecycleData, &lifecycleData)
	if err != nil {
		return &models.TaskDefinition{}, "", "", err
	}

	err = backend.validateRequest(request, lifecycleData)
	if err != nil {
		return &models.TaskDefinition{}, "", "", err
	}

	compilerURL, err := backend.compilerDownloadURL()
	if err != nil {
		return &models.TaskDefinition{}, "", "", err
	}

	cachedDependencies := []*models.CachedDependency{
		&models.CachedDependency{
			From:     compilerURL.String(),
			To:       path.Dir(DockerBuilderExecutablePath),
			CacheKey: "docker-lifecycle",
		},
	}

	runActionArguments := []string{
		"-outputMetadataJSONFilename", DockerBuilderOutputPath,
		"-dockerRef", lifecycleData.DockerImageUrl,
	}

	if len(backend.config.InsecureDockerRegistries) > 0 {
		insecureDockerRegistries := strings.Join(backend.config.InsecureDockerRegistries, ",")
		runActionArguments = append(runActionArguments, "-insecureDockerRegistries", insecureDockerRegistries)
	}

	fileDescriptorLimit := uint64(request.FileDescriptors)
	runAs := "vcap"

	actions := []models.ActionInterface{}

	if cacheDockerImage(request.Environment) {
		runAs = "root"

		additionalEgressRules, additionalArgs, err := cachingEgressRulesAndArgs(
			logger,
			backend.config.DockerRegistryAddress,
			backend.config.ConsulCluster,
			lifecycleData,
		)
		if err != nil {
			return &models.TaskDefinition{}, "", "", err
		}

		runActionArguments = append(runActionArguments, additionalArgs...)
		request.EgressRules = append(request.EgressRules, additionalEgressRules...)

		actions = append(
			actions,
			models.EmitProgressFor(
				&models.RunAction{
					Path: MountCgroupsPath,
					ResourceLimits: &models.ResourceLimits{
						Nofile: &fileDescriptorLimit,
					},
					User: runAs,
				},
				"Preparing docker daemon...",
				"",
				"Failed to set up docker environment",
			),
		)
	}

	actions = append(
		actions,
		models.EmitProgressFor(
			&models.RunAction{
				Path: DockerBuilderExecutablePath,
				Args: runActionArguments,
				Env:  request.Environment,
				ResourceLimits: &models.ResourceLimits{
					Nofile: &fileDescriptorLimit,
				},
				User: runAs,
			},
			"Staging...",
			"Staging Complete",
			"Staging Failed",
		),
	)

	annotationJson, _ := json.Marshal(cc_messages.StagingTaskAnnotation{
		Lifecycle:          DockerLifecycleName,
		CompletionCallback: request.CompletionCallback,
	})

	taskDefinition := &models.TaskDefinition{
		RootFs:                        models.PreloadedRootFS(backend.config.DockerStagingStack),
		ResultFile:                    DockerBuilderOutputPath,
		Privileged:                    true,
		MemoryMb:                      int32(request.MemoryMB),
		LogSource:                     TaskLogSource,
		LogGuid:                       request.LogGuid,
		EgressRules:                   request.EgressRules,
		DiskMb:                        int32(request.DiskMB),
		CompletionCallbackUrl:         backend.config.CallbackURL(stagingGuid),
		Annotation:                    string(annotationJson),
		Action:                        models.WrapAction(models.Timeout(models.Serial(actions...), dockerTimeout(request, backend.logger))),
		CachedDependencies:            cachedDependencies,
		LegacyDownloadUser:            "******",
		TrustedSystemCertificatesPath: TrustedSystemCertificatesPath,
	}
	logger.Debug("staging-task-request")

	return taskDefinition, stagingGuid, backend.config.TaskDomain, nil
}
		handler := handlers.NewDesireAppHandler(logger, fakeBBS, map[string]recipebuilder.RecipeBuilder{
			"buildpack": buildpackBuilder,
			"docker":    dockerBuilder,
		})
		handler.DesireApp(responseRecorder, request)
	})

	Context("when the desired LRP does not exist", func() {
		var newlyDesiredLRP *models.DesiredLRP

		BeforeEach(func() {
			newlyDesiredLRP = &models.DesiredLRP{
				ProcessGuid: "new-process-guid",
				Instances:   1,
				RootFs:      models.PreloadedRootFS("stack-2"),
				Action: models.WrapAction(&models.RunAction{
					User: "******",
					Path: "ls",
				}),
				Annotation: "last-modified-etag",
			}

			fakeBBS.DesiredLRPByProcessGuidReturns(&models.DesiredLRP{}, models.ErrResourceNotFound)
			buildpackBuilder.BuildReturns(newlyDesiredLRP, nil)
		})

		It("logs the incoming and outgoing request", func() {
			Eventually(logger.TestSink.Buffer).Should(gbytes.Say("request-from-cc"))
			Eventually(logger.TestSink.Buffer).Should(gbytes.Say("creating-desired-lrp"))
		})
Example #4
0
func (backend *dockerBackend) BuildRecipe(stagingGuid string, request cc_messages.StagingRequestFromCC) (*models.TaskDefinition, string, string, error) {
	logger := backend.logger.Session("build-recipe", lager.Data{"app-id": request.AppId, "staging-guid": stagingGuid})
	logger.Info("staging-request")

	var lifecycleData cc_messages.DockerStagingData
	err := json.Unmarshal(*request.LifecycleData, &lifecycleData)
	if err != nil {
		return &models.TaskDefinition{}, "", "", err
	}

	err = backend.validateRequest(request, lifecycleData)
	if err != nil {
		return &models.TaskDefinition{}, "", "", err
	}

	compilerURL, err := backend.compilerDownloadURL()
	if err != nil {
		return &models.TaskDefinition{}, "", "", err
	}

	cacheDockerImage := false
	for _, envVar := range request.Environment {
		if envVar.Name == "DIEGO_DOCKER_CACHE" && envVar.Value == "true" {
			cacheDockerImage = true
			break
		}
	}

	actions := []models.ActionInterface{}

	//Download builder
	actions = append(
		actions,
		models.EmitProgressFor(
			&models.DownloadAction{
				From:     compilerURL.String(),
				To:       path.Dir(DockerBuilderExecutablePath),
				CacheKey: "docker-lifecycle",
				User:     "******",
			},
			"",
			"",
			"Failed to set up docker environment",
		),
	)

	runActionArguments := []string{"-outputMetadataJSONFilename", DockerBuilderOutputPath, "-dockerRef", lifecycleData.DockerImageUrl}
	runAs := "vcap"
	if cacheDockerImage {
		runAs = "root"

		host, port, err := net.SplitHostPort(backend.config.DockerRegistryAddress)
		if err != nil {
			logger.Debug("invalid docker registry address", lager.Data{"address": backend.config.DockerRegistryAddress, "error": err.Error()})
			return &models.TaskDefinition{}, "", "", ErrInvalidDockerRegistryAddress
		}

		registryServices, err := getDockerRegistryServices(backend.config.ConsulCluster, backend.logger)
		if err != nil {
			return &models.TaskDefinition{}, "", "", err
		}
		registryRules := addDockerRegistryRules(request.EgressRules, registryServices)
		request.EgressRules = append(request.EgressRules, registryRules...)

		registryIPs := strings.Join(buildDockerRegistryAddresses(registryServices), ",")

		runActionArguments, err = addDockerCachingArguments(runActionArguments, registryIPs, backend.config.InsecureDockerRegistry, host, port, lifecycleData)
		if err != nil {
			return &models.TaskDefinition{}, "", "", err
		}
	}

	fileDescriptorLimit := uint64(request.FileDescriptors)

	// Run builder
	actions = append(
		actions,
		models.EmitProgressFor(
			&models.RunAction{
				Path: DockerBuilderExecutablePath,
				Args: runActionArguments,
				Env:  request.Environment,
				ResourceLimits: &models.ResourceLimits{
					Nofile: &fileDescriptorLimit,
				},
				User: runAs,
			},
			"Staging...",
			"Staging Complete",
			"Staging Failed",
		),
	)

	annotationJson, _ := json.Marshal(cc_messages.StagingTaskAnnotation{
		Lifecycle: DockerLifecycleName,
	})

	taskDefinition := &models.TaskDefinition{
		RootFs:                models.PreloadedRootFS(backend.config.DockerStagingStack),
		ResultFile:            DockerBuilderOutputPath,
		Privileged:            true,
		MemoryMb:              int32(request.MemoryMB),
		LogSource:             TaskLogSource,
		LogGuid:               request.LogGuid,
		EgressRules:           request.EgressRules,
		DiskMb:                int32(request.DiskMB),
		CompletionCallbackUrl: backend.config.CallbackURL(stagingGuid),
		Annotation:            string(annotationJson),
		Action:                models.WrapAction(models.Timeout(models.Serial(actions...), dockerTimeout(request, backend.logger))),
	}
	logger.Debug("staging-task-request")

	return taskDefinition, stagingGuid, backend.config.TaskDomain, nil
}
	"fmt"
	"sync"
	"time"

	"github.com/cloudfoundry-incubator/auction/simulation/util"
	"github.com/cloudfoundry-incubator/auction/simulation/visualization"
	"github.com/cloudfoundry-incubator/auctioneer"
	"github.com/cloudfoundry-incubator/bbs/models"
	"github.com/cloudfoundry-incubator/rep"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Auction", func() {
	var initialDistributions map[int][]rep.LRP
	var linuxRootFSURL = models.PreloadedRootFS(linuxStack)

	newLRP := func(processGuid string, index int, memoryMB int) rep.LRP {
		lrpKey := models.NewActualLRPKey(processGuid, int32(index), "domain")
		return rep.NewLRP(lrpKey, rep.NewResource(int32(memoryMB), 1, linuxRootFSURL))
	}

	generateUniqueLRPs := func(numInstances int, index int, memoryMB int) []rep.LRP {
		instances := []rep.LRP{}
		for i := 0; i < numInstances; i++ {
			instances = append(instances, newLRP(util.NewGrayscaleGuid("AAA"), index, memoryMB))
		}
		return instances
	}

	newLRPStartAuction := func(processGuid string, index int, memoryMB int32) auctioneer.LRPStartRequest {
Example #6
0
	auctions := make([]auctiontypes.LRPAuction, 0, len(start.Indices))
	for _, index := range start.Indices {
		lrpKey := models.NewActualLRPKey(start.ProcessGuid, int32(index), start.Domain)
		auctions = append(auctions, auctiontypes.NewLRPAuction(rep.NewLRP(lrpKey, start.Resource), queueTime))
	}

	return auctions
}

func BuildTaskAuction(task *rep.Task, queueTime time.Time) auctiontypes.TaskAuction {
	return auctiontypes.NewTaskAuction(*task, queueTime)
}

const linuxStack = "linux"

var linuxRootFSURL = models.PreloadedRootFS(linuxStack)

var linuxOnlyRootFSProviders = rep.RootFSProviders{models.PreloadedRootFSScheme: rep.NewFixedSetRootFSProvider(linuxStack)}

const windowsStack = "windows"

var windowsRootFSURL = models.PreloadedRootFS(windowsStack)

var windowsOnlyRootFSProviders = rep.RootFSProviders{models.PreloadedRootFSScheme: rep.NewFixedSetRootFSProvider(windowsStack)}

func BuildCellState(
	zone string,
	memoryMB int32,
	diskMB int32,
	containers int,
	evacuating bool,
Example #7
0
					lrps, err := bbsClient.DesiredLRPs(models.DesiredLRPFilter{})
					Expect(err).NotTo(HaveOccurred())

					result := []*models.DesiredLRP{}
					for _, lrp := range lrps {
						lrp.ModificationTag = nil
						result = append(result, lrp)
					}
					return result
				}

				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},
Example #8
0
		actions := actionsFromTaskDef(taskDef)
		Expect(actions).To(HaveLen(2))
		Expect(actions[0].GetEmitProgressAction()).To(Equal(downloadBuilderAction))
		Expect(actions[1].GetEmitProgressAction()).To(Equal(runAction))

		Expect(taskDef.MemoryMb).To(Equal(memoryMb))
		Expect(taskDef.DiskMb).To(Equal(diskMb))
		Expect(taskDef.EgressRules).To(ConsistOf(egressRules))
	})

	It("uses the configured docker staging stack", func() {
		taskDef, _, _, err := docker.BuildRecipe(stagingGuid, stagingRequest)
		Expect(err).NotTo(HaveOccurred())

		Expect(taskDef.RootFs).To(Equal(models.PreloadedRootFS("penguin")))
	})

	It("gives the task a callback URL to call it back", func() {
		taskDef, _, _, err := docker.BuildRecipe(stagingGuid, stagingRequest)
		Expect(err).NotTo(HaveOccurred())

		Expect(taskDef.CompletionCallbackUrl).To(Equal(fmt.Sprintf("%s/v1/staging/%s/completed", config.StagerURL, stagingGuid)))
	})

	Describe("staging action timeout", func() {
		Context("when a positive timeout is specified in the staging request from CC", func() {
			BeforeEach(func() {
				timeout = 5
			})
func (backend *traditionalBackend) BuildRecipe(stagingGuid string, request cc_messages.StagingRequestFromCC) (*models.TaskDefinition, string, string, error) {
	logger := backend.logger.Session("build-recipe", lager.Data{"app-id": request.AppId, "staging-guid": stagingGuid})
	logger.Info("staging-request")

	if request.LifecycleData == nil {
		return &models.TaskDefinition{}, "", "", ErrMissingLifecycleData
	}

	var lifecycleData cc_messages.BuildpackStagingData
	err := json.Unmarshal(*request.LifecycleData, &lifecycleData)
	if err != nil {
		return &models.TaskDefinition{}, "", "", err
	}

	err = backend.validateRequest(request, lifecycleData)
	if err != nil {
		return &models.TaskDefinition{}, "", "", err
	}

	compilerURL, err := backend.compilerDownloadURL(request, lifecycleData)
	if err != nil {
		return &models.TaskDefinition{}, "", "", err
	}

	buildpacksOrder := []string{}
	for _, buildpack := range lifecycleData.Buildpacks {
		buildpacksOrder = append(buildpacksOrder, buildpack.Key)
	}

	skipDetect := len(lifecycleData.Buildpacks) == 1 && lifecycleData.Buildpacks[0].SkipDetect

	builderConfig := buildpack_app_lifecycle.NewLifecycleBuilderConfig(buildpacksOrder, skipDetect, backend.config.SkipCertVerify)

	timeout := traditionalTimeout(request, backend.logger)

	actions := []models.ActionInterface{}

	//Download app package
	appDownloadAction := &models.DownloadAction{
		Artifact: "app package",
		From:     lifecycleData.AppBitsDownloadUri,
		To:       builderConfig.BuildDir(),
		User:     "******",
	}

	actions = append(actions, appDownloadAction)

	downloadActions := []models.ActionInterface{}
	downloadNames := []string{}

	//Download builder
	downloadActions = append(
		downloadActions,
		models.EmitProgressFor(
			&models.DownloadAction{
				From:     compilerURL.String(),
				To:       path.Dir(builderConfig.ExecutablePath),
				CacheKey: fmt.Sprintf("buildpack-%s-lifecycle", lifecycleData.Stack),
				User:     "******",
			},
			"",
			"",
			"Failed to set up staging environment",
		),
	)

	//Download buildpacks
	buildpackNames := []string{}
	downloadMsgPrefix := ""
	if !skipDetect {
		downloadMsgPrefix = "No buildpack specified; fetching standard buildpacks to detect and build your application.\n"
	}
	for _, buildpack := range lifecycleData.Buildpacks {
		if buildpack.Name == cc_messages.CUSTOM_BUILDPACK {
			buildpackNames = append(buildpackNames, buildpack.Url)
		} else {
			buildpackNames = append(buildpackNames, buildpack.Name)
			downloadActions = append(
				downloadActions,
				&models.DownloadAction{
					Artifact: buildpack.Name,
					From:     buildpack.Url,
					To:       builderConfig.BuildpackPath(buildpack.Key),
					CacheKey: buildpack.Key,
					User:     "******",
				},
			)
		}
	}

	downloadNames = append(downloadNames, fmt.Sprintf("buildpacks (%s)", strings.Join(buildpackNames, ", ")))

	//Download buildpack artifacts cache
	downloadURL, err := backend.buildArtifactsDownloadURL(lifecycleData)
	if err != nil {
		return &models.TaskDefinition{}, "", "", err
	}

	if downloadURL != nil {
		downloadActions = append(
			downloadActions,
			models.Try(
				&models.DownloadAction{
					Artifact: "build artifacts cache",
					From:     downloadURL.String(),
					To:       builderConfig.BuildArtifactsCacheDir(),
					User:     "******",
				},
			),
		)
		downloadNames = append(downloadNames, "build artifacts cache")
	}

	downloadMsg := downloadMsgPrefix + fmt.Sprintf("Downloading %s...", strings.Join(downloadNames, ", "))
	actions = append(actions, models.EmitProgressFor(models.Parallel(downloadActions...), downloadMsg, "Downloaded buildpacks", "Downloading buildpacks failed"))

	fileDescriptorLimit := uint64(request.FileDescriptors)

	//Run Builder
	runEnv := append(request.Environment, &models.EnvironmentVariable{"CF_STACK", lifecycleData.Stack})
	actions = append(
		actions,
		models.EmitProgressFor(
			&models.RunAction{
				User: "******",
				Path: builderConfig.Path(),
				Args: builderConfig.Args(),
				Env:  runEnv,
				ResourceLimits: &models.ResourceLimits{
					Nofile: &fileDescriptorLimit,
				},
			},
			"Staging...",
			"Staging complete",
			"Staging failed",
		),
	)

	//Upload Droplet
	uploadActions := []models.ActionInterface{}
	uploadNames := []string{}
	uploadURL, err := backend.dropletUploadURL(request, lifecycleData)
	if err != nil {
		return &models.TaskDefinition{}, "", "", err
	}

	uploadActions = append(
		uploadActions,
		&models.UploadAction{
			Artifact: "droplet",
			From:     builderConfig.OutputDroplet(), // get the droplet
			To:       addTimeoutParamToURL(*uploadURL, timeout).String(),
			User:     "******",
		},
	)
	uploadNames = append(uploadNames, "droplet")

	//Upload Buildpack Artifacts Cache
	uploadURL, err = backend.buildArtifactsUploadURL(request, lifecycleData)
	if err != nil {
		return &models.TaskDefinition{}, "", "", err
	}

	uploadActions = append(uploadActions,
		models.Try(
			&models.UploadAction{
				Artifact: "build artifacts cache",
				From:     builderConfig.OutputBuildArtifactsCache(), // get the compressed build artifacts cache
				To:       addTimeoutParamToURL(*uploadURL, timeout).String(),
				User:     "******",
			},
		),
	)
	uploadNames = append(uploadNames, "build artifacts cache")

	uploadMsg := fmt.Sprintf("Uploading %s...", strings.Join(uploadNames, ", "))
	actions = append(actions, models.EmitProgressFor(models.Parallel(uploadActions...), uploadMsg, "Uploading complete", "Uploading failed"))

	annotationJson, _ := json.Marshal(cc_messages.StagingTaskAnnotation{
		Lifecycle:          TraditionalLifecycleName,
		CompletionCallback: request.CompletionCallback,
	})

	taskDefinition := &models.TaskDefinition{
		RootFs:                models.PreloadedRootFS(lifecycleData.Stack),
		ResultFile:            builderConfig.OutputMetadata(),
		MemoryMb:              int32(request.MemoryMB),
		DiskMb:                int32(request.DiskMB),
		CpuWeight:             uint32(StagingTaskCpuWeight),
		Action:                models.WrapAction(models.Timeout(models.Serial(actions...), timeout)),
		LogGuid:               request.LogGuid,
		LogSource:             TaskLogSource,
		CompletionCallbackUrl: backend.config.CallbackURL(stagingGuid),
		EgressRules:           request.EgressRules,
		Annotation:            string(annotationJson),
		Privileged:            true,
		EnvironmentVariables:  []*models.EnvironmentVariable{{"LANG", DefaultLANG}},
	}

	logger.Debug("staging-task-request")

	return taskDefinition, stagingGuid, backend.config.TaskDomain, nil
}
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
}
func (b *BuildpackRecipeBuilder) BuildTask(task *cc_messages.TaskRequestFromCC) (*models.TaskDefinition, error) {
	logger := b.logger.Session("build-task", lager.Data{"request": task})

	if task.DropletUri == "" {
		logger.Error("missing-droplet-source", ErrDropletSourceMissing)
		return nil, ErrDropletSourceMissing
	}

	if task.DockerPath != "" {
		logger.Error("invalid-docker-path", ErrMultipleAppSources)
		return nil, ErrMultipleAppSources
	}

	downloadAction := &models.DownloadAction{
		From:     task.DropletUri,
		To:       ".",
		CacheKey: "",
		User:     "******",
	}

	runAction := &models.RunAction{
		User:           "******",
		Path:           "/tmp/lifecycle/launcher",
		Args:           []string{"app", task.Command, ""},
		Env:            task.EnvironmentVariables,
		LogSource:      "TASK",
		ResourceLimits: &models.ResourceLimits{},
	}

	var lifecycle = "buildpack/" + task.RootFs
	lifecyclePath, ok := b.config.Lifecycles[lifecycle]
	if !ok {
		logger.Error("unknown-lifecycle", ErrNoLifecycleDefined, lager.Data{
			"lifecycle": lifecycle,
		})

		return nil, ErrNoLifecycleDefined
	}

	lifecycleURL := lifecycleDownloadURL(lifecyclePath, b.config.FileServerURL)

	cachedDependencies := []*models.CachedDependency{
		&models.CachedDependency{
			From:     lifecycleURL,
			To:       "/tmp/lifecycle",
			CacheKey: fmt.Sprintf("%s-lifecycle", strings.Replace(lifecycle, "/", "-", 1)),
		},
	}

	rootFSPath := models.PreloadedRootFS(task.RootFs)

	taskDefinition := &models.TaskDefinition{
		Privileged:            true,
		LogGuid:               task.LogGuid,
		MemoryMb:              int32(task.MemoryMb),
		DiskMb:                int32(task.DiskMb),
		CpuWeight:             cpuWeight(task.MemoryMb),
		EnvironmentVariables:  task.EnvironmentVariables,
		RootFs:                rootFSPath,
		CompletionCallbackUrl: task.CompletionCallbackUrl,
		Action: models.WrapAction(models.Serial(
			downloadAction,
			runAction,
		)),
		CachedDependencies: cachedDependencies,
		EgressRules:        task.EgressRules,
		LegacyDownloadUser: "******",
	}

	return taskDefinition, nil
}
			})

			It("returns an error", func() {
				_, _, _, err := traditional.BuildRecipe(stagingGuid, stagingRequest)
				Expect(err).To(Equal(backend.ErrMissingLifecycleData))
			})
		})
	})

	It("creates a cf-app-staging Task with staging instructions", func() {
		taskDef, guid, domain, err := traditional.BuildRecipe(stagingGuid, stagingRequest)
		Expect(err).NotTo(HaveOccurred())

		Expect(domain).To(Equal("config-task-domain"))
		Expect(guid).To(Equal(stagingGuid))
		Expect(taskDef.RootFs).To(Equal(models.PreloadedRootFS("rabbit_hole")))
		Expect(taskDef.LogGuid).To(Equal("bunny"))
		Expect(taskDef.MetricsGuid).To(BeEmpty()) // do not emit metrics for staging!
		Expect(taskDef.LogSource).To(Equal(backend.TaskLogSource))
		Expect(taskDef.ResultFile).To(Equal("/tmp/result.json"))
		Expect(taskDef.Privileged).To(BeTrue())

		var annotation cc_messages.StagingTaskAnnotation
		err = json.Unmarshal([]byte(taskDef.Annotation), &annotation)
		Expect(err).NotTo(HaveOccurred())

		Expect(annotation).To(Equal(cc_messages.StagingTaskAnnotation{
			Lifecycle:          "buildpack",
			CompletionCallback: "https://api.cc.com/v1/staging/some-staging-guid/droplet_completed",
		}))
		})

		Context("when everything is correct", func() {
			It("does not error", func() {
				Expect(err).NotTo(HaveOccurred())
			})

			It("builds a valid DesiredLRP", func() {
				Expect(desiredLRP.ProcessGuid).To(Equal("the-app-guid-the-app-version"))
				Expect(desiredLRP.Instances).To(BeEquivalentTo(23))
				Expect(*desiredLRP.Routes).To(Equal(cfroutes.CFRoutes{
					{Hostnames: []string{"route1", "route2"}, Port: 8080},
				}.RoutingInfo()))

				Expect(desiredLRP.Annotation).To(Equal("etag-updated-at"))
				Expect(desiredLRP.RootFs).To(Equal(models.PreloadedRootFS("some-stack")))
				Expect(desiredLRP.MemoryMb).To(BeEquivalentTo(128))
				Expect(desiredLRP.DiskMb).To(BeEquivalentTo(512))
				Expect(desiredLRP.Ports).To(Equal([]uint32{8080}))
				Expect(desiredLRP.Privileged).To(BeTrue())
				Expect(desiredLRP.StartTimeout).To(BeEquivalentTo(123456))

				Expect(desiredLRP.LogGuid).To(Equal("the-log-id"))
				Expect(desiredLRP.LogSource).To(Equal("CELL"))

				Expect(desiredLRP.EnvironmentVariables).To(ConsistOf(&models.EnvironmentVariable{"LANG", recipebuilder.DefaultLANG}))

				Expect(desiredLRP.MetricsGuid).To(Equal("the-log-id"))

				expectedSetup := models.Serial(
					&models.DownloadAction{