func describeMultilineError() { var err error It("returns a simple single-line message string (depth=0)", func() { err = bosherr.Error("omg") Expect(MultilineError(err)).To(Equal("omg")) }) Context("when given a composite error", func() { It("returns a multi-line, indented message string (depth=1)", func() { err = bosherr.WrapError(bosherr.Error("inner omg"), "omg") Expect(MultilineError(err)).To(Equal("omg:\n inner omg")) }) It("returns a multi-line, indented message string (depth=2)", func() { err = bosherr.WrapError(bosherr.WrapError(bosherr.Error("inner omg"), "omg"), "outer omg") Expect(MultilineError(err)).To(Equal("outer omg:\n omg:\n inner omg")) }) It("returns a multi-line, indented message string (depth=3)", func() { err = bosherr.WrapError(bosherr.WrapError(bosherr.WrapError(bosherr.Error("inner omg"), "almost inner omg"), "almost outer omg"), "outer omg") Expect(MultilineError(err)).To(Equal("outer omg:\n almost outer omg:\n almost inner omg:\n inner omg")) }) }) Context("when given an explainable error", func() { It("returns a multi-line message string with sibling errors at the same indentation", func() { err = bosherr.NewMultiError(bosherr.Error("a"), bosherr.Error("b")) Expect(MultilineError(err)).To(Equal("a\nb")) }) It("returns a multi-line message string with sibling errors at the same indentation", func() { complex := bosherr.WrapError(bosherr.Error("inner a"), "outer a") err = bosherr.NewMultiError(complex, bosherr.Error("b")) Expect(MultilineError(err)).To(Equal("outer a:\n inner a\nb")) }) It("returns a multi-line message string with sibling errors at the same indentation", func() { complex := bosherr.WrapError(bosherr.Error("inner b"), "outer b") err = bosherr.NewMultiError(bosherr.Error("a"), complex) Expect(MultilineError(err)).To(Equal("a\nouter b:\n inner b")) }) }) Context("when given a composite err with explainable errors", func() { It("returns a multi-line message string with sibling errors at the same indentation", func() { multi := bosherr.NewMultiError(bosherr.Error("inner a"), bosherr.Error("inner b")) err = bosherr.WrapError(multi, "outer omg") Expect(MultilineError(err)).To(Equal("outer omg:\n inner a\n inner b")) }) }) Context("when given an ExecError", func() { It("returns a multi-line message string with the command, stdout, & stderr at the same indentation", func() { execErr := boshsys.NewExecError("fake-cmd --flag with some args", "some\nmultiline\nstdout", "some\nmultiline\nstderr") err = bosherr.WrapError(execErr, "outer omg") Expect(MultilineError(err)).To(Equal("outer omg:\n Error Executing Command:\n fake-cmd --flag with some args\n StdOut:\n some\n multiline\n stdout\n StdErr:\n some\n multiline\n stderr")) }) }) }
func (v *validator) Validate(release Release) error { errs := []error{} err := v.validateReleaseName(release) if err != nil { errs = append(errs, bosherr.WrapError(err, "Validating release name")) } err = v.validateReleaseVersion(release) if err != nil { errs = append(errs, bosherr.WrapError(err, "Validating release version")) } err = v.validateReleaseJobs(release) if err != nil { errs = append(errs, bosherr.WrapError(err, "Validating release jobs")) } err = v.validateReleasePackages(release) if err != nil { errs = append(errs, bosherr.WrapError(err, "Validating release packages")) } if len(errs) > 0 { return bosherr.NewMultiError(errs...) } return nil }
func (r *reader) newReleaseFromManifest(releaseManifest birelmanifest.Manifest) (Release, error) { errors := []error{} packages, isCompiledRelease, err := r.newPackagesFromManifestPackages(releaseManifest) if err != nil { errors = append(errors, bosherr.WrapError(err, "Constructing packages from manifest")) } jobs, err := r.newJobsFromManifestJobs(packages, releaseManifest.Jobs) if err != nil { errors = append(errors, bosherr.WrapError(err, "Constructing jobs from manifest")) } if len(errors) > 0 { return nil, bosherr.NewMultiError(errors...) } release := &release{ name: releaseManifest.Name, version: releaseManifest.Version, jobs: jobs, packages: packages, extractedPath: r.extractedReleasePath, fs: r.fs, isCompiled: isCompiledRelease, } return release, nil }
func (v *validator) validateReleaseJobs(release Release) error { errs := []error{} for _, job := range release.Jobs() { if job.Name == "" { errs = append(errs, errors.New("Job name is missing")) } if job.Fingerprint == "" { errs = append(errs, fmt.Errorf("Job '%s' fingerprint is missing", job.Name)) } if job.SHA1 == "" { errs = append(errs, fmt.Errorf("Job '%s' sha1 is missing", job.Name)) } monitPath := path.Join(job.ExtractedPath, "monit") if !v.fs.FileExists(monitPath) { errs = append(errs, fmt.Errorf("Job '%s' is missing monit file", job.Name)) } for template := range job.Templates { templatePath := path.Join(job.ExtractedPath, "templates", template) if !v.fs.FileExists(templatePath) { errs = append(errs, fmt.Errorf("Job '%s' is missing template '%s'", job.Name, templatePath)) } } for _, pkgName := range job.PackageNames { found := false for _, releasePackage := range release.Packages() { if releasePackage.Name == pkgName { found = true break } } if !found { errs = append(errs, fmt.Errorf("Job '%s' requires '%s' which is not in the release", job.Name, pkgName)) } } } if len(errs) > 0 { return bosherr.NewMultiError(errs...) } return nil }
func (v *validator) validateReleasePackages(release Release) error { errs := []error{} stemcells := map[string]string{} for _, pkg := range release.Packages() { if pkg.Name == "" { errs = append(errs, errors.New("Package name is missing")) } if pkg.Fingerprint == "" { errs = append(errs, fmt.Errorf("Package '%s' fingerprint is missing", pkg.Name)) } if pkg.SHA1 == "" { errs = append(errs, fmt.Errorf("Package '%s' sha1 is missing", pkg.Name)) } if release.IsCompiled() { if pkg.Stemcell == "" { errs = append(errs, fmt.Errorf("Compiled package '%s' stemcell is missing", pkg.Name)) } else { stemcells[pkg.Stemcell] = pkg.Stemcell } } } if release.IsCompiled() && len(stemcells) > 1 { keys := []string{} for k := range stemcells { keys = append(keys, k) } sort.Strings(keys) errs = append(errs, fmt.Errorf("Packages were compiled against different stemcells: %v", keys)) } if len(errs) > 0 { return bosherr.NewMultiError(errs...) } return nil }
func (r *reader) newJobsFromManifestJobs(packages []*birelpkg.Package, manifestJobs []birelmanifest.JobRef) ([]bireljob.Job, error) { jobs := []bireljob.Job{} errors := []error{} for _, manifestJob := range manifestJobs { extractedJobPath := path.Join(r.extractedReleasePath, "extracted_jobs", manifestJob.Name) err := r.fs.MkdirAll(extractedJobPath, os.ModeDir|0700) if err != nil { errors = append(errors, bosherr.WrapError(err, "Creating extracted job path")) continue } jobArchivePath := path.Join(r.extractedReleasePath, "jobs", manifestJob.Name+".tgz") jobReader := bireljob.NewReader(jobArchivePath, extractedJobPath, r.extractor, r.fs) job, err := jobReader.Read() if err != nil { errors = append(errors, bosherr.WrapErrorf(err, "Reading job '%s' from archive", manifestJob.Name)) continue } job.Fingerprint = manifestJob.Fingerprint job.SHA1 = manifestJob.SHA1 for _, pkgName := range job.PackageNames { pkg, found := r.findPackageByName(packages, pkgName) if !found { return []bireljob.Job{}, bosherr.Errorf("Package not found: '%s'", pkgName) } job.Packages = append(job.Packages, pkg) } jobs = append(jobs, job) } if len(errors) > 0 { return []bireljob.Job{}, bosherr.NewMultiError(errors...) } return jobs, nil }
func (v *validator) ValidateReleaseJobs(deploymentManifest Manifest, releaseManager birel.Manager) error { errs := []error{} for idx, job := range deploymentManifest.Jobs { for templateIdx, template := range job.Templates { release, found := releaseManager.Find(template.Release) if !found { errs = append(errs, bosherr.Errorf("jobs[%d].templates[%d].release '%s' must refer to release in releases", idx, templateIdx, template.Release)) } else { _, found := release.FindJobByName(template.Name) if !found { errs = append(errs, bosherr.Errorf("jobs[%d].templates[%d] must refer to a job in '%s', but there is no job named '%s'", idx, templateIdx, release.Name(), template.Name)) } } } } if len(errs) > 0 { return bosherr.NewMultiError(errs...) } return nil }
func (v *validator) Validate(manifest Manifest) error { errs := []error{} releaseNames := map[string]struct{}{} if len(manifest.Releases) < 1 { errs = append(errs, bosherr.Errorf("releases must contain at least 1 release")) } for releaseIdx, release := range manifest.Releases { if v.isBlank(release.Name) { errs = append(errs, bosherr.Errorf("releases[%d].name must be provided", releaseIdx)) } if _, found := releaseNames[release.Name]; found { errs = append(errs, bosherr.Errorf("releases[%d].name '%s' must be unique", releaseIdx, release.Name)) } releaseNames[release.Name] = struct{}{} if v.isBlank(release.URL) { errs = append(errs, bosherr.Errorf("releases[%d].url must be provided", releaseIdx)) } matched, err := regexp.MatchString("^(file|http|https)://", release.URL) if err != nil || !matched { errs = append(errs, bosherr.Errorf("releases[%d].url must be a valid URL (file:// or http(s)://)", releaseIdx)) } if strings.HasPrefix(release.URL, "http") && v.isBlank(release.SHA1) { errs = append(errs, bosherr.Errorf("releases[%d].sha1 must be provided for http URL", releaseIdx)) } } if len(errs) > 0 { return bosherr.NewMultiError(errs...) } return nil }
func (v *validator) Validate(manifest Manifest, releaseSetManifest birelsetmanifest.Manifest) error { errs := []error{} cpiJobName := manifest.Template.Name if v.isBlank(cpiJobName) { errs = append(errs, bosherr.Error("cloud_provider.template.name must be provided")) } cpiReleaseName := manifest.Template.Release if v.isBlank(cpiReleaseName) { errs = append(errs, bosherr.Error("cloud_provider.template.release must be provided")) } _, found := releaseSetManifest.FindByName(cpiReleaseName) if !found { errs = append(errs, bosherr.Errorf("cloud_provider.template.release '%s' must refer to a release in releases", cpiReleaseName)) } if len(errs) > 0 { return bosherr.NewMultiError(errs...) } return nil }
func (r *reader) newPackagesFromManifestPackages(releaseManifest birelmanifest.Manifest) ([]*birelpkg.Package, bool, error) { manifestPackages := releaseManifest.Packages isCompiledPackage := false if len(releaseManifest.Packages) > 0 && len(releaseManifest.CompiledPackages) > 0 { return []*birelpkg.Package{}, isCompiledPackage, bosherr.Errorf("Release '%s' contains compiled and non-compiled pacakges", releaseManifest.Name) } else if len(releaseManifest.CompiledPackages) > 0 { manifestPackages = releaseManifest.CompiledPackages isCompiledPackage = true } packages := []*birelpkg.Package{} errors := []error{} packageRepo := &birelpkg.PackageRepo{} packagesDirectory := "packages" if isCompiledPackage { packagesDirectory = "compiled_packages" } for _, manifestPackage := range manifestPackages { pkg := packageRepo.FindOrCreatePackage(manifestPackage.Name) extractedPackagePath := path.Join(r.extractedReleasePath, "extracted_packages", manifestPackage.Name) err := r.fs.MkdirAll(extractedPackagePath, os.ModeDir|0700) if err != nil { errors = append(errors, bosherr.WrapError(err, "Creating extracted package path")) continue } packageArchivePath := path.Join(r.extractedReleasePath, packagesDirectory, manifestPackage.Name+".tgz") err = r.extractor.DecompressFileToDir(packageArchivePath, extractedPackagePath, boshcmd.CompressorOptions{}) if err != nil { errors = append(errors, bosherr.WrapErrorf(err, "Extracting package '%s'", manifestPackage.Name)) continue } pkg.Fingerprint = manifestPackage.Fingerprint pkg.SHA1 = manifestPackage.SHA1 pkg.ExtractedPath = extractedPackagePath pkg.ArchivePath = packageArchivePath if isCompiledPackage { pkg.Stemcell = manifestPackage.Stemcell } pkg.Dependencies = []*birelpkg.Package{} for _, manifestPackageName := range manifestPackage.Dependencies { pkg.Dependencies = append(pkg.Dependencies, packageRepo.FindOrCreatePackage(manifestPackageName)) } packages = append(packages, pkg) } if len(errors) > 0 { return []*birelpkg.Package{}, isCompiledPackage, bosherr.NewMultiError(errors...) } return packages, isCompiledPackage, nil }
func (v *validator) Validate(deploymentManifest Manifest, releaseSetManifest birelsetmanifest.Manifest) error { errs := []error{} if v.isBlank(deploymentManifest.Name) { errs = append(errs, bosherr.Error("name must be provided")) } networksErrors := v.validateNetworks(deploymentManifest.Networks) errs = append(errs, networksErrors...) for idx, resourcePool := range deploymentManifest.ResourcePools { if v.isBlank(resourcePool.Name) { errs = append(errs, bosherr.Errorf("resource_pools[%d].name must be provided", idx)) } if v.isBlank(resourcePool.Network) { errs = append(errs, bosherr.Errorf("resource_pools[%d].network must be provided", idx)) } else if _, ok := v.networkNames(deploymentManifest)[resourcePool.Network]; !ok { errs = append(errs, bosherr.Errorf("resource_pools[%d].network must be the name of a network", idx)) } if v.isBlank(resourcePool.Stemcell.URL) { errs = append(errs, bosherr.Errorf("resource_pools[%d].stemcell.url must be provided", idx)) } matched, err := regexp.MatchString("^(file|http|https)://", resourcePool.Stemcell.URL) if err != nil || !matched { errs = append(errs, bosherr.Errorf("resource_pools[%d].stemcell.url must be a valid URL (file:// or http(s)://)", idx)) } if strings.HasPrefix(resourcePool.Stemcell.URL, "http") && v.isBlank(resourcePool.Stemcell.SHA1) { errs = append(errs, bosherr.Errorf("resource_pools[%d].stemcell.sha1 must be provided for http URL", idx)) } } for idx, diskPool := range deploymentManifest.DiskPools { if v.isBlank(diskPool.Name) { errs = append(errs, bosherr.Errorf("disk_pools[%d].name must be provided", idx)) } if diskPool.DiskSize <= 0 { errs = append(errs, bosherr.Errorf("disk_pools[%d].disk_size must be > 0", idx)) } } if len(deploymentManifest.Jobs) > 1 { errs = append(errs, bosherr.Error("jobs must be of size 1")) } for idx, job := range deploymentManifest.Jobs { if v.isBlank(job.Name) { errs = append(errs, bosherr.Errorf("jobs[%d].name must be provided", idx)) } if job.PersistentDisk < 0 { errs = append(errs, bosherr.Errorf("jobs[%d].persistent_disk must be >= 0", idx)) } if job.PersistentDiskPool != "" { if _, ok := v.diskPoolNames(deploymentManifest)[job.PersistentDiskPool]; !ok { errs = append(errs, bosherr.Errorf("jobs[%d].persistent_disk_pool must be the name of a disk pool", idx)) } } if job.Instances < 0 { errs = append(errs, bosherr.Errorf("jobs[%d].instances must be >= 0", idx)) } if len(job.Networks) == 0 { errs = append(errs, bosherr.Errorf("jobs[%d].networks must be a non-empty array", idx)) } if v.isBlank(job.ResourcePool) { errs = append(errs, bosherr.Errorf("jobs[%d].resource_pool must be provided", idx)) } else { if _, ok := v.resourcePoolNames(deploymentManifest)[job.ResourcePool]; !ok { errs = append(errs, bosherr.Errorf("jobs[%d].resource_pool must be the name of a resource pool", idx)) } } errs = append(errs, v.validateJobNetworks(job.Networks, deploymentManifest.Networks, idx)...) if job.Lifecycle != "" && job.Lifecycle != JobLifecycleService { errs = append(errs, bosherr.Errorf("jobs[%d].lifecycle must be 'service' ('%s' not supported)", idx, job.Lifecycle)) } templateNames := map[string]struct{}{} for templateIdx, template := range job.Templates { if v.isBlank(template.Name) { errs = append(errs, bosherr.Errorf("jobs[%d].templates[%d].name must be provided", idx, templateIdx)) } if _, found := templateNames[template.Name]; found { errs = append(errs, bosherr.Errorf("jobs[%d].templates[%d].name '%s' must be unique", idx, templateIdx, template.Name)) } templateNames[template.Name] = struct{}{} if v.isBlank(template.Release) { errs = append(errs, bosherr.Errorf("jobs[%d].templates[%d].release must be provided", idx, templateIdx)) } else { _, found := releaseSetManifest.FindByName(template.Release) if !found { errs = append(errs, bosherr.Errorf("jobs[%d].templates[%d].release '%s' must refer to release in releases", idx, templateIdx, template.Release)) } } } } if len(errs) > 0 { return bosherr.NewMultiError(errs...) } return nil }