// Upload stemcell to an IAAS. It does the following steps: // 1) uploads the stemcell to the cloud (if needed), // 2) saves a record of the uploaded stemcell in the repo func (m *manager) Upload(extractedStemcell ExtractedStemcell, uploadStage biui.Stage) (cloudStemcell CloudStemcell, err error) { manifest := extractedStemcell.Manifest() stageName := fmt.Sprintf("Uploading stemcell '%s/%s'", manifest.Name, manifest.Version) err = uploadStage.Perform(stageName, func() error { foundStemcellRecord, found, err := m.repo.Find(manifest.Name, manifest.Version) if err != nil { return bosherr.WrapError(err, "Finding existing stemcell record in repo") } if found { cloudStemcell = NewCloudStemcell(foundStemcellRecord, m.repo, m.cloud) return biui.NewSkipStageError(bosherr.Errorf("Found stemcell: %#v", foundStemcellRecord), "Stemcell already uploaded") } cid, err := m.cloud.CreateStemcell(manifest.ImagePath, manifest.CloudProperties) if err != nil { return bosherr.WrapErrorf(err, "creating stemcell (%s %s)", manifest.Name, manifest.Version) } stemcellRecord, err := m.repo.Save(manifest.Name, manifest.Version, cid) if err != nil { //TODO: delete stemcell from cloud when saving fails return bosherr.WrapErrorf(err, "saving stemcell record in repo (cid=%s, stemcell=%s)", cid, extractedStemcell) } cloudStemcell = NewCloudStemcell(stemcellRecord, m.repo, m.cloud) return nil }) if err != nil { return cloudStemcell, err } return cloudStemcell, nil }
func (i *instance) UpdateJobs( deploymentManifest bideplmanifest.Manifest, stage biui.Stage, ) error { newState, err := i.stateBuilder.Build(i.jobName, i.id, deploymentManifest, stage) if err != nil { return bosherr.WrapErrorf(err, "Building state for instance '%s/%d'", i.jobName, i.id) } stepName := fmt.Sprintf("Updating instance '%s/%d'", i.jobName, i.id) err = stage.Perform(stepName, func() error { err := i.vm.Stop() if err != nil { return bosherr.WrapError(err, "Stopping the agent") } err = i.vm.Apply(newState.ToApplySpec()) if err != nil { return bosherr.WrapError(err, "Applying the agent state") } err = i.vm.Start() if err != nil { return bosherr.WrapError(err, "Starting the agent") } return nil }) if err != nil { return err } return i.waitUntilJobsAreRunning(deploymentManifest.Update.UpdateWatchTime, stage) }
func (i *instance) shutdown( pingTimeout time.Duration, pingDelay time.Duration, stage biui.Stage, ) error { stepName := fmt.Sprintf("Waiting for the agent on VM '%s'", i.vm.CID()) waitingForAgentErr := stage.Perform(stepName, func() error { if err := i.vm.WaitUntilReady(pingTimeout, pingDelay); err != nil { return bosherr.WrapError(err, "Agent unreachable") } return nil }) if waitingForAgentErr != nil { i.logger.Warn(i.logTag, "Gave up waiting for agent: %s", waitingForAgentErr.Error()) return nil } if err := i.stopJobs(stage); err != nil { return err } if err := i.unmountDisks(stage); err != nil { return err } return nil }
func (i *instance) Delete( pingTimeout time.Duration, pingDelay time.Duration, stage biui.Stage, ) error { vmExists, err := i.vm.Exists() if err != nil { return bosherr.WrapErrorf(err, "Checking existance of vm for instance '%s/%d'", i.jobName, i.id) } if vmExists { if err = i.shutdown(pingTimeout, pingDelay, stage); err != nil { return err } } // non-existent VMs still need to be 'deleted' to clean up related resources owned by the CPI stepName := fmt.Sprintf("Deleting VM '%s'", i.vm.CID()) return stage.Perform(stepName, func() error { err := i.vm.Delete() cloudErr, ok := err.(bicloud.Error) if ok && cloudErr.Type() == bicloud.VMNotFoundError { return biui.NewSkipStageError(cloudErr, "VM not found") } return err }) }
func (s Fetcher) GetStemcell(deploymentManifest bideplmanifest.Manifest, stage biui.Stage) (ExtractedStemcell, error) { stemcell, err := deploymentManifest.Stemcell(deploymentManifest.JobName()) if err != nil { return nil, err } stemcellTarballPath, err := s.TarballProvider.Get(stemcell, stage) if err != nil { return nil, err } var extractedStemcell ExtractedStemcell err = stage.Perform("Validating stemcell", func() error { extractedStemcell, err = s.StemcellExtractor.Extract(stemcellTarballPath) if err != nil { return bosherr.WrapErrorf(err, "Extracting stemcell from '%s'", stemcellTarballPath) } return nil }) if err != nil { return nil, err } return extractedStemcell, nil }
// renderJobTemplates renders all the release job templates for multiple release jobs specified // by a deployment job and randomly uploads them to blobstore func (b *jobRenderer) renderJobTemplates( releaseJobs []bireljob.Job, releaseJobProperties map[string]*biproperty.Map, jobProperties biproperty.Map, globalProperties biproperty.Map, deploymentName string, stage biui.Stage, ) ([]RenderedJobRef, error) { renderedJobRefs := make([]RenderedJobRef, 0, len(releaseJobs)) err := stage.Perform("Rendering job templates", func() error { renderedJobList, err := b.jobListRenderer.Render(releaseJobs, releaseJobProperties, jobProperties, globalProperties, deploymentName, "") if err != nil { return err } defer renderedJobList.DeleteSilently() for _, renderedJob := range renderedJobList.All() { renderedJobRef, err := b.compressAndUpload(renderedJob) if err != nil { return err } renderedJobRefs = append(renderedJobRefs, renderedJobRef) } return nil }) return renderedJobRefs, err }
func (y DeploymentManifestParser) GetDeploymentManifest(deploymentManifestPath string, releaseSetManifest birelsetmanifest.Manifest, stage biui.Stage) (bideplmanifest.Manifest, error) { var deploymentManifest bideplmanifest.Manifest err := stage.Perform("Validating deployment manifest", func() error { var err error deploymentManifest, err = y.DeploymentParser.Parse(deploymentManifestPath) if err != nil { return bosherr.WrapErrorf(err, "Parsing deployment manifest '%s'", deploymentManifestPath) } err = y.DeploymentValidator.Validate(deploymentManifest, releaseSetManifest) if err != nil { return bosherr.WrapError(err, "Validating deployment manifest") } err = y.DeploymentValidator.ValidateReleaseJobs(deploymentManifest, y.ReleaseManager) if err != nil { return bosherr.WrapError(err, "Validating deployment jobs refer to jobs in release") } return nil }) if err != nil { return bideplmanifest.Manifest{}, err } return deploymentManifest, nil }
// compilePackages compiles the specified packages, in the order specified, uploads them to the Blobstore, and returns the blob references func (c *dependencyCompiler) compilePackages(requiredPackages []*birelpkg.Package, stage biui.Stage) ([]CompiledPackageRef, error) { packageRefs := make([]CompiledPackageRef, 0, len(requiredPackages)) for _, pkg := range requiredPackages { stepName := fmt.Sprintf("Compiling package '%s/%s'", pkg.Name, pkg.Fingerprint) err := stage.Perform(stepName, func() error { compiledPackageRecord, err := c.packageCompiler.Compile(pkg) if err != nil { return err } packageRef := CompiledPackageRef{ Name: pkg.Name, Version: pkg.Fingerprint, BlobstoreID: compiledPackageRecord.BlobID, SHA1: compiledPackageRecord.BlobSHA1, } packageRefs = append(packageRefs, packageRef) return nil }) if err != nil { return nil, err } } return packageRefs, nil }
func (i *installation) stopRegistryNice(logger boshlog.Logger, stage biui.Stage) { err := stage.Perform("Stopping registry", func() error { return i.StopRegistry() }) if err != nil { logger.Warn("installation", "Registry failed to stop: %s", err) } }
func (d *diskDeployer) attachDisk(disk bidisk.Disk, vm VM, stage biui.Stage) error { stageName := fmt.Sprintf("Attaching disk '%s' to VM '%s'", disk.CID(), vm.CID()) err := stage.Perform(stageName, func() error { return vm.AttachDisk(disk) }) return err }
func (d *diskDeployer) createDisk(diskPool bideplmanifest.DiskPool, vm VM, stage biui.Stage) (disk bidisk.Disk, err error) { err = stage.Perform("Creating disk", func() error { disk, err = d.diskManager.Create(diskPool, vm.CID()) return err }) return disk, err }
func (i *installation) WithRunningRegistry(logger boshlog.Logger, stage biui.Stage, fn func() error) error { err := stage.Perform("Starting registry", func() error { return i.StartRegistry() }) if err != nil { return err } defer i.stopRegistryNice(logger, stage) return fn() }
func (d *diskDeployer) migrateDisk( originalDisk bidisk.Disk, diskPool bideplmanifest.DiskPool, vm VM, stage biui.Stage, ) (newDisk bidisk.Disk, err error) { d.logger.Debug(d.logTag, "Migrating disk '%s'", originalDisk.CID()) err = stage.Perform("Creating disk", func() error { newDisk, err = d.diskManager.Create(diskPool, vm.CID()) return err }) if err != nil { return newDisk, err } stageName := fmt.Sprintf("Attaching disk '%s' to VM '%s'", newDisk.CID(), vm.CID()) err = stage.Perform(stageName, func() error { return vm.AttachDisk(newDisk) }) if err != nil { return newDisk, err } stageName = fmt.Sprintf("Migrating disk content from '%s' to '%s'", originalDisk.CID(), newDisk.CID()) err = stage.Perform(stageName, func() error { return vm.MigrateDisk() }) if err != nil { return newDisk, err } err = d.updateCurrentDiskRecord(newDisk) if err != nil { return newDisk, err } stageName = fmt.Sprintf("Detaching disk '%s'", originalDisk.CID()) err = stage.Perform(stageName, func() error { return vm.DetachDisk(originalDisk) }) if err != nil { return newDisk, err } stageName = fmt.Sprintf("Deleting disk '%s'", originalDisk.CID()) err = stage.Perform(stageName, func() error { return originalDisk.Delete() }) if err != nil { return newDisk, err } return newDisk, nil }
func (d *deployment) deleteDisk(deleteStage biui.Stage, disk bidisk.Disk) error { stepName := fmt.Sprintf("Deleting disk '%s'", disk.CID()) return deleteStage.Perform(stepName, func() error { err := disk.Delete() cloudErr, ok := err.(bicloud.Error) if ok && cloudErr.Type() == bicloud.DiskNotFoundError { return biui.NewSkipStageError(cloudErr, "Disk not found") } return err }) }
func (d *deployment) deleteStemcell(deleteStage biui.Stage, stemcell bistemcell.CloudStemcell) error { stepName := fmt.Sprintf("Deleting stemcell '%s'", stemcell.CID()) return deleteStage.Perform(stepName, func() error { err := stemcell.Delete() cloudErr, ok := err.(bicloud.Error) if ok && cloudErr.Type() == bicloud.StemcellNotFoundError { return biui.NewSkipStageError(cloudErr, "Stemcell not found") } return err }) }
func (i *instance) waitUntilJobsAreRunning(updateWatchTime bideplmanifest.WatchTime, stage biui.Stage) error { start := time.Duration(updateWatchTime.Start) * time.Millisecond end := time.Duration(updateWatchTime.End) * time.Millisecond delayBetweenAttempts := 1 * time.Second maxAttempts := int((end - start) / delayBetweenAttempts) stepName := fmt.Sprintf("Waiting for instance '%s/%d' to be running", i.jobName, i.id) return stage.Perform(stepName, func() error { time.Sleep(start) return i.vm.WaitToBeRunning(maxAttempts, delayBetweenAttempts) }) }
func (i CpiInstaller) installCpiRelease(installer biinstall.Installer, installationManifest biinstallmanifest.Manifest, target biinstall.Target, stage biui.Stage) (biinstall.Installation, error) { var installation biinstall.Installation var err error err = stage.PerformComplex("installing CPI", func(installStage biui.Stage) error { installation, err = installer.Install(installationManifest, installStage) return err }) if err != nil { return installation, bosherr.WrapError(err, "Installing CPI") } return installation, nil }
func (i CpiInstaller) ValidateCpiRelease(installationManifest biinstallmanifest.Manifest, stage biui.Stage) error { return stage.Perform("Validating cpi release", func() error { cpiReleaseName := installationManifest.Template.Release cpiRelease, found := i.ReleaseManager.Find(cpiReleaseName) if !found { return bosherr.Errorf("installation release '%s' must refer to a provided release", cpiReleaseName) } err := i.Validator.Validate(cpiRelease, installationManifest.Template.Name) if err != nil { return bosherr.WrapErrorf(err, "Invalid CPI release '%s'", cpiReleaseName) } return nil }) }
func (c *deploymentDeleter) findCurrentDeploymentAndDelete(stage biui.Stage, deploymentManager bidepl.Manager) error { c.logger.Debug(c.logTag, "Finding current deployment...") deployment, found, err := deploymentManager.FindCurrent() if err != nil { return bosherr.WrapError(err, "Finding current deployment") } return stage.PerformComplex("deleting deployment", func(deleteStage biui.Stage) error { if !found { //TODO: skip? would require adding skip support to PerformComplex c.logger.Debug(c.logTag, "No current deployment found...") return nil } return deployment.Delete(deleteStage) }) }
func (p *provider) Get(source Source, stage biui.Stage) (string, error) { if strings.HasPrefix(source.GetURL(), "file://") { filePath := strings.TrimPrefix(source.GetURL(), "file://") expandedPath, err := p.fs.ExpandPath(filePath) if err != nil { p.logger.Warn(p.logTag, "Failed to expand file path %s, using original URL", filePath) return filePath, nil } p.logger.Debug(p.logTag, "Using the tarball from file source: '%s'", filePath) return expandedPath, nil } if !strings.HasPrefix(source.GetURL(), "http") { return "", bosherr.Errorf("Invalid source URL: '%s', must be either file:// or http(s)://", source.GetURL()) } var cachedPath string err := stage.Perform(fmt.Sprintf("Downloading %s", source.Description()), func() error { var found bool cachedPath, found = p.cache.Get(source) if found { p.logger.Debug(p.logTag, "Using the tarball from cache: '%s'", cachedPath) return biui.NewSkipStageError(bosherr.Error("Already downloaded"), "Found in local cache") } retryStrategy := boshretry.NewAttemptRetryStrategy(p.downloadAttempts, p.delayTimeout, p.downloadRetryable(source), p.logger) err := retryStrategy.Try() if err != nil { return bosherr.WrapErrorf(err, "Failed to download from '%s'", source.GetURL()) } p.logger.Debug(p.logTag, "Using the downloaded tarball: '%s'", cachedPath) return nil }) if err != nil { return "", err } return p.cache.Path(source), nil }
// renderJobTemplates renders all the release job templates for multiple release jobs specified by a deployment job func (b *builder) renderJobTemplates( releaseJobs []bireljob.Job, releaseJobProperties map[string]*biproperty.Map, jobProperties biproperty.Map, globalProperties biproperty.Map, deploymentName string, address string, stage biui.Stage, ) (renderedJobs, error) { var ( renderedJobListArchive bitemplate.RenderedJobListArchive blobID string ) err := stage.Perform("Rendering job templates", func() error { renderedJobList, err := b.jobListRenderer.Render(releaseJobs, releaseJobProperties, jobProperties, globalProperties, deploymentName, address) if err != nil { return err } defer renderedJobList.DeleteSilently() renderedJobListArchive, err = b.renderedJobListCompressor.Compress(renderedJobList) if err != nil { return bosherr.WrapError(err, "Compressing rendered job templates") } defer renderedJobListArchive.DeleteSilently() blobID, err = b.blobstore.Add(renderedJobListArchive.Path()) if err != nil { return bosherr.WrapErrorf(err, "Uploading rendered job template archive '%s' to the blobstore", renderedJobListArchive.Path()) } return nil }) if err != nil { return renderedJobs{}, err } return renderedJobs{ BlobstoreID: blobID, Archive: renderedJobListArchive, }, nil }
func (i *installer) installJob(renderedJobRef RenderedJobRef, stage biui.Stage) (installedJob InstalledJob, err error) { err = stage.Perform(fmt.Sprintf("Installing job '%s'", renderedJobRef.Name), func() error { var stageErr error jobDir := filepath.Join(i.target.JobsPath(), renderedJobRef.Name) stageErr = i.blobExtractor.Extract(renderedJobRef.BlobstoreID, renderedJobRef.SHA1, jobDir) if stageErr != nil { return bosherr.WrapErrorf(stageErr, "Extracting blob with ID '%s'", renderedJobRef.BlobstoreID) } stageErr = i.blobExtractor.ChmodExecutables(path.Join(jobDir, "bin", "*")) if stageErr != nil { return bosherr.WrapErrorf(stageErr, "Chmoding binaries for '%s'", jobDir) } installedJob = NewInstalledJob(renderedJobRef, jobDir) return nil }) return installedJob, err }
func (i *instance) unmountDisks(stage biui.Stage) error { disks, err := i.vm.Disks() if err != nil { return bosherr.WrapErrorf(err, "Getting VM '%s' disks", i.vm.CID()) } for _, disk := range disks { stepName := fmt.Sprintf("Unmounting disk '%s'", disk.CID()) err = stage.Perform(stepName, func() error { if err := i.vm.UnmountDisk(disk); err != nil { return bosherr.WrapErrorf(err, "Unmounting disk '%s' from VM '%s'", disk.CID(), i.vm.CID()) } return nil }) if err != nil { return err } } return nil }
func (f Fetcher) DownloadAndExtract(releaseRef manifest.ReleaseRef, stage ui.Stage) error { releasePath, err := f.tarballProvider.Get(releaseRef, stage) if err != nil { return err } err = stage.Perform(fmt.Sprintf("Validating release '%s'", releaseRef.Name), func() error { release, err := f.releaseExtractor.Extract(releasePath) if err != nil { return bosherr.WrapErrorf(err, "Extracting release '%s'", releasePath) } if release.Name() != releaseRef.Name { return bosherr.Errorf("Release name '%s' does not match the name in release tarball '%s'", releaseRef.Name, release.Name()) } f.releaseManager.Add(release) return nil }) return err }
func (m *manager) Create( jobName string, id int, deploymentManifest bideplmanifest.Manifest, cloudStemcell bistemcell.CloudStemcell, registryConfig biinstallmanifest.Registry, eventLoggerStage biui.Stage, ) (Instance, []bidisk.Disk, error) { var vm bivm.VM stepName := fmt.Sprintf("Creating VM for instance '%s/%d' from stemcell '%s'", jobName, id, cloudStemcell.CID()) err := eventLoggerStage.Perform(stepName, func() error { var err error vm, err = m.vmManager.Create(cloudStemcell, deploymentManifest) if err != nil { return bosherr.WrapError(err, "Creating VM") } if err = cloudStemcell.PromoteAsCurrent(); err != nil { return bosherr.WrapErrorf(err, "Promoting stemcell as current '%s'", cloudStemcell.CID()) } return nil }) if err != nil { return nil, []bidisk.Disk{}, err } instance := m.instanceFactory.NewInstance(jobName, id, vm, m.vmManager, m.sshTunnelFactory, m.blobstore, m.logger) if err := instance.WaitUntilReady(registryConfig, eventLoggerStage); err != nil { return instance, []bidisk.Disk{}, bosherr.WrapError(err, "Waiting until instance is ready") } disks, err := instance.UpdateDisks(deploymentManifest, eventLoggerStage) if err != nil { return instance, disks, bosherr.WrapError(err, "Updating instance disks") } return instance, disks, err }
func (i *installer) Install(manifest biinstallmanifest.Manifest, stage biui.Stage) (Installation, error) { i.logger.Info(i.logTag, "Installing CPI deployment '%s'", manifest.Name) i.logger.Debug(i.logTag, "Installing CPI deployment '%s' with manifest: %#v", manifest.Name, manifest) jobs, err := i.jobResolver.From(manifest) if err != nil { return nil, bosherr.WrapError(err, "Resolving jobs from manifest") } compiledPackages, err := i.packageCompiler.For(jobs, stage) if err != nil { return nil, err } err = stage.Perform("Installing packages", func() error { return i.installPackages(compiledPackages) }) if err != nil { return nil, err } renderedJobRefs, err := i.jobRenderer.RenderAndUploadFrom(manifest, jobs, stage) if err != nil { return nil, bosherr.WrapError(err, "Rendering and uploading Jobs") } renderedCPIJob := renderedJobRefs[0] installedJob, err := i.installJob(renderedCPIJob, stage) if err != nil { return nil, bosherr.WrapErrorf(err, "Installing job '%s' for CPI release", renderedCPIJob.Name) } return NewInstallation( i.target, installedJob, manifest, i.registryServerManager, ), nil }
func (m *manager) DeleteUnused(eventLoggerStage biui.Stage) error { disks, err := m.FindUnused() if err != nil { return bosherr.WrapError(err, "Finding unused disks") } for _, disk := range disks { stepName := fmt.Sprintf("Deleting unused disk '%s'", disk.CID()) err = eventLoggerStage.Perform(stepName, func() error { err := disk.Delete() cloudErr, ok := err.(bicloud.Error) if ok && cloudErr.Type() == bicloud.DiskNotFoundError { return biui.NewSkipStageError(cloudErr, "Disk Not Found") } return err }) if err != nil { return err } } return nil }
func (m *manager) DeleteUnused(deleteStage biui.Stage) error { stemcells, err := m.FindUnused() if err != nil { return bosherr.WrapError(err, "Finding unused stemcells") } for _, stemcell := range stemcells { stepName := fmt.Sprintf("Deleting unused stemcell '%s'", stemcell.CID()) err = deleteStage.Perform(stepName, func() error { err := stemcell.Delete() cloudErr, ok := err.(bicloud.Error) if ok && cloudErr.Type() == bicloud.StemcellNotFoundError { return biui.NewSkipStageError(cloudErr, "Stemcell not found") } return err }) if err != nil { return err } } return nil }
func (i *instance) WaitUntilReady( registryConfig biinstallmanifest.Registry, stage biui.Stage, ) error { stepName := fmt.Sprintf("Waiting for the agent on VM '%s' to be ready", i.vm.CID()) err := stage.Perform(stepName, func() error { if !registryConfig.IsEmpty() { sshTunnelOptions := bisshtunnel.Options{ Host: registryConfig.SSHTunnel.Host, Port: registryConfig.SSHTunnel.Port, User: registryConfig.SSHTunnel.User, Password: registryConfig.SSHTunnel.Password, PrivateKey: registryConfig.SSHTunnel.PrivateKey, LocalForwardPort: registryConfig.Port, RemoteForwardPort: registryConfig.Port, } sshTunnel := i.sshTunnelFactory.NewSSHTunnel(sshTunnelOptions) sshReadyErrCh := make(chan error) sshErrCh := make(chan error) go sshTunnel.Start(sshReadyErrCh, sshErrCh) defer func() { if err := sshTunnel.Stop(); err != nil { i.logger.Warn(i.logTag, "Failed to stop ssh tunnel: %s", err.Error()) } }() err := <-sshReadyErrCh if err != nil { return bosherr.WrapError(err, "Starting SSH tunnel") } } return i.vm.WaitUntilReady(10*time.Minute, 500*time.Millisecond) }) return err }
func (i *instance) stopJobs(stage biui.Stage) error { stepName := fmt.Sprintf("Stopping jobs on instance '%s/%d'", i.jobName, i.id) return stage.Perform(stepName, func() error { return i.vm.Stop() }) }