func main() { sharedWriter := &logWriter{ writer: os.Stderr, } logger := log.New(sharedWriter, "", log.LstdFlags) configPath := flag.String("c", "", "Path to the JSON configuration file") machineImagePath := flag.String("image", "", "Path to the input machine image (root.img)") machineImageFormat := flag.String("format", resources.VolumeRawFormat, "Format of the input machine image (RAW or vmdk). Defaults to RAW.") imageVolumeSize := flag.Int("volume-size", 0, "Block device size (in GB) of the input machine image") manifestPath := flag.String("manifest", "", "Path to the input stemcell.MF") flag.Parse() if *configPath == "" { usage("-c flag is required") } if *machineImagePath == "" { usage("--image flag is required") } if *manifestPath == "" { usage("--manifest flag is required") } if *imageVolumeSize == 0 && *machineImageFormat != resources.VolumeRawFormat { usage("--volume-size flag is required for formats other than RAW") } configFile, err := os.Open(*configPath) if err != nil { logger.Fatalf("Error opening config file: %s", err) } defer func() { closeErr := configFile.Close() if closeErr != nil { logger.Fatalf("Error closing config file: %s", closeErr) } }() if err != nil { logger.Fatalf("Error opening config file: %s", err) } c, err := config.NewFromReader(configFile) if err != nil { logger.Fatalf("Error parsing config file: %s. Message: %s", *configPath, err) } if _, err := os.Stat(*machineImagePath); os.IsNotExist(err) { logger.Fatalf("machine image not found at: %s", *machineImagePath) } if _, err := os.Stat(*manifestPath); os.IsNotExist(err) { logger.Fatalf("manifest not found at: %s", *manifestPath) } manifestBytes, err := ioutil.ReadFile(*manifestPath) if err != nil { logger.Fatalf("opening manifest: %s", err) } m, err := manifest.NewFromReader(bytes.NewReader(manifestBytes)) if err != nil { logger.Fatalf("reading manifest: %s", err) } amiCollection := collection.Ami{} errCollection := collection.Error{} var wg sync.WaitGroup wg.Add(len(c.AmiRegions)) imageConfig := publisher.MachineImageConfig{ LocalPath: *machineImagePath, FileFormat: *machineImageFormat, VolumeSizeGB: int64(*imageVolumeSize), } for i := range c.AmiRegions { go func(regionConfig config.AmiRegion) { defer wg.Done() switch { case regionConfig.IsolatedRegion: ds := driverset.NewIsolatedRegionDriverSet(sharedWriter, regionConfig.Credentials) p := publisher.NewIsolatedRegionPublisher(sharedWriter, publisher.Config{ AmiRegion: regionConfig, AmiConfiguration: c.AmiConfiguration, }) amis, err := p.Publish(ds, imageConfig) if err != nil { errCollection.Add(fmt.Errorf("Error publishing AMIs to %s: %s", regionConfig.RegionName, err)) } else { amiCollection.Merge(amis) } default: ds := driverset.NewStandardRegionDriverSet(sharedWriter, regionConfig.Credentials) p := publisher.NewStandardRegionPublisher(sharedWriter, publisher.Config{ AmiRegion: regionConfig, AmiConfiguration: c.AmiConfiguration, }) amis, err := p.Publish(ds, imageConfig) if err != nil { errCollection.Add(fmt.Errorf("Error publishing AMIs to %s: %s", regionConfig.RegionName, err)) } else { amiCollection.Merge(amis) } } }(c.AmiRegions[i]) } logger.Println("Waiting for publishers to finish...") wg.Wait() combinedErr := errCollection.Error() if combinedErr != nil { logger.Fatal(combinedErr) } m.PublishedAmis = amiCollection.GetAll() m.Sha1 = shasum([]byte{}) err = m.Write(os.Stdout) if err != nil { logger.Fatalf("writing manifest: %s", err) } logger.Println("Publishing finished successfully") }
func (p *StandardRegionPublisher) Publish(ds driverset.StandardRegionDriverSet, machineImageConfig MachineImageConfig) (*collection.Ami, error) { createStartTime := time.Now() defer func(startTime time.Time) { p.logger.Printf("completed Publish() in %f minutes\n", time.Since(startTime).Minutes()) }(createStartTime) machineImageDriverConfig := resources.MachineImageDriverConfig{ MachineImagePath: machineImageConfig.LocalPath, FileFormat: machineImageConfig.FileFormat, BucketName: p.BucketName, } machineImageDriver := ds.MachineImageDriver() machineImage, err := machineImageDriver.Create(machineImageDriverConfig) if err != nil { return nil, fmt.Errorf("creating machine image: %s", err) } defer func() { err := machineImageDriver.Delete(machineImage) if err != nil { p.logger.Printf("Failed to delete machine image %s: %s", machineImage.GetURL, err) } }() snapshotDriverConfig := resources.SnapshotDriverConfig{ MachineImageURL: machineImage.GetURL, FileFormat: machineImageConfig.FileFormat, } snapshotDriver := ds.CreateSnapshotDriver() snapshot, err := snapshotDriver.Create(snapshotDriverConfig) if err != nil { return nil, fmt.Errorf("creating snapshot: %s", err) } createAmiDriver := ds.CreateAmiDriver() createAmiDriverConfig := resources.AmiDriverConfig{ SnapshotID: snapshot.ID, AmiProperties: p.AmiProperties, } sourceAmi, err := createAmiDriver.Create(createAmiDriverConfig) if err != nil { return nil, fmt.Errorf("creating ami: %s", err) } amis := collection.Ami{ VirtualizationType: p.AmiProperties.VirtualizationType, } amis.Add(sourceAmi) copyAmiDriver := ds.CopyAmiDriver() procGroup := sync.WaitGroup{} procGroup.Add(len(p.CopyDestinations)) errCol := collection.Error{} for i := range p.CopyDestinations { go func(dstRegion string) { defer procGroup.Done() copyAmiDriverConfig := resources.AmiDriverConfig{ ExistingAmiID: sourceAmi.ID, DestinationRegion: dstRegion, AmiProperties: p.AmiProperties, } copiedAmi, copyErr := copyAmiDriver.Create(copyAmiDriverConfig) if copyErr != nil { errCol.Add(fmt.Errorf("copying source ami: %s to destination region: %s: %s", sourceAmi.ID, dstRegion, copyErr)) return } amis.Add(copiedAmi) }(p.CopyDestinations[i]) } procGroup.Wait() return &amis, errCol.Error() }
package collection_test import ( "errors" "light-stemcell-builder/collection" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Error", func() { It("outputs an error with expected messages for all the errors collected", func() { e := collection.Error{} e.Add(errors.New("The quick brown")) e.Add(errors.New("fox jumps over")) e.Add(errors.New("the lazy dog")) err := e.Error() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("The quick brown")) Expect(err.Error()).To(ContainSubstring("fox jumps over")) Expect(err.Error()).To(ContainSubstring("the lazy dog")) }) It("does not output an error when no errors have been added", func() { e := collection.Error{} err := e.Error() Expect(err).ToNot(HaveOccurred()) }) })