1package stemcell 2 3import ( 4 "fmt" 5 "path/filepath" 6 7 bicloud "github.com/cloudfoundry/bosh-cli/cloud" 8 biconfig "github.com/cloudfoundry/bosh-cli/config" 9 biui "github.com/cloudfoundry/bosh-cli/ui" 10 bosherr "github.com/cloudfoundry/bosh-utils/errors" 11) 12 13type Manager interface { 14 FindCurrent() ([]CloudStemcell, error) 15 Upload(ExtractedStemcell, biui.Stage) (CloudStemcell, error) 16 FindUnused() ([]CloudStemcell, error) 17 DeleteUnused(biui.Stage) error 18} 19 20type manager struct { 21 repo biconfig.StemcellRepo 22 cloud bicloud.Cloud 23} 24 25func NewManager(repo biconfig.StemcellRepo, cloud bicloud.Cloud) Manager { 26 return &manager{ 27 repo: repo, 28 cloud: cloud, 29 } 30} 31 32func (m *manager) FindCurrent() ([]CloudStemcell, error) { 33 stemcells := []CloudStemcell{} 34 35 stemcellRecord, found, err := m.repo.FindCurrent() 36 if err != nil { 37 return stemcells, bosherr.WrapError(err, "Reading stemcell record") 38 } 39 40 if found { 41 stemcell := NewCloudStemcell(stemcellRecord, m.repo, m.cloud) 42 stemcells = append(stemcells, stemcell) 43 } 44 45 return stemcells, nil 46} 47 48// Upload stemcell to an IAAS. It does the following steps: 49// 1) uploads the stemcell to the cloud (if needed), 50// 2) saves a record of the uploaded stemcell in the repo 51func (m *manager) Upload(extractedStemcell ExtractedStemcell, uploadStage biui.Stage) (cloudStemcell CloudStemcell, err error) { 52 manifest := extractedStemcell.Manifest() 53 stageName := fmt.Sprintf("Uploading stemcell '%s/%s'", manifest.Name, manifest.Version) 54 err = uploadStage.Perform(stageName, func() error { 55 foundStemcellRecord, found, err := m.repo.Find(manifest.Name, manifest.Version) 56 if err != nil { 57 return bosherr.WrapError(err, "Finding existing stemcell record in repo") 58 } 59 60 if found { 61 cloudStemcell = NewCloudStemcell(foundStemcellRecord, m.repo, m.cloud) 62 return biui.NewSkipStageError(bosherr.Errorf("Found stemcell: %#v", foundStemcellRecord), "Stemcell already uploaded") 63 } 64 65 cid, err := m.cloud.CreateStemcell(filepath.Join(extractedStemcell.GetExtractedPath(), "image"), manifest.CloudProperties) 66 if err != nil { 67 return bosherr.WrapErrorf(err, "creating stemcell (%s %s)", manifest.Name, manifest.Version) 68 } 69 70 stemcellRecord, err := m.repo.Save(manifest.Name, manifest.Version, cid, manifest.ApiVersion) 71 if err != nil { 72 //TODO: delete stemcell from cloud when saving fails 73 return bosherr.WrapErrorf(err, "saving stemcell record in repo (cid=%s, stemcell=%s)", cid, extractedStemcell) 74 } 75 76 cloudStemcell = NewCloudStemcell(stemcellRecord, m.repo, m.cloud) 77 return nil 78 }) 79 if err != nil { 80 return cloudStemcell, err 81 } 82 83 return cloudStemcell, nil 84} 85 86func (m *manager) FindUnused() ([]CloudStemcell, error) { 87 unusedStemcells := []CloudStemcell{} 88 89 stemcellRecords, err := m.repo.All() 90 if err != nil { 91 return unusedStemcells, bosherr.WrapError(err, "Getting all stemcell records") 92 } 93 94 currentStemcellRecord, found, err := m.repo.FindCurrent() 95 if err != nil { 96 return unusedStemcells, bosherr.WrapError(err, "Finding current disk record") 97 } 98 99 for _, stemcellRecord := range stemcellRecords { 100 if !found || stemcellRecord.ID != currentStemcellRecord.ID { 101 stemcell := NewCloudStemcell(stemcellRecord, m.repo, m.cloud) 102 unusedStemcells = append(unusedStemcells, stemcell) 103 } 104 } 105 106 return unusedStemcells, nil 107} 108 109func (m *manager) DeleteUnused(deleteStage biui.Stage) error { 110 stemcells, err := m.FindUnused() 111 if err != nil { 112 return bosherr.WrapError(err, "Finding unused stemcells") 113 } 114 115 for _, stemcell := range stemcells { 116 stepName := fmt.Sprintf("Deleting unused stemcell '%s'", stemcell.CID()) 117 err = deleteStage.Perform(stepName, func() error { 118 err := stemcell.Delete() 119 cloudErr, ok := err.(bicloud.Error) 120 if ok && cloudErr.Type() == bicloud.StemcellNotFoundError { 121 return biui.NewSkipStageError(cloudErr, "Stemcell not found") 122 } 123 return err 124 }) 125 if err != nil { 126 return err 127 } 128 } 129 130 return nil 131} 132