func (lb *FileBackend) BuildACI(layerIDs []string, dockerURL *types.ParsedDockerURL, outputDir string, tmpBaseDir string, compression common.Compression) ([]string, []*schema.ImageManifest, error) { if strings.Contains(layerIDs[0], ":") { return lb.BuildACIV22(layerIDs, dockerURL, outputDir, tmpBaseDir, compression) } var aciLayerPaths []string var aciManifests []*schema.ImageManifest var curPwl []string tmpDir, err := ioutil.TempDir(tmpBaseDir, "docker2aci-") if err != nil { return nil, nil, fmt.Errorf("error creating dir: %v", err) } defer os.RemoveAll(tmpDir) for i := len(layerIDs) - 1; i >= 0; i-- { if err := common.ValidateLayerId(layerIDs[i]); err != nil { return nil, nil, err } j, err := getJson(lb.file, layerIDs[i]) if err != nil { return nil, nil, fmt.Errorf("error getting layer json: %v", err) } layerData := types.DockerImageData{} if err := json.Unmarshal(j, &layerData); err != nil { return nil, nil, fmt.Errorf("error unmarshaling layer data: %v", err) } tmpLayerPath := path.Join(tmpDir, layerIDs[i]) tmpLayerPath += ".tar" layerTarPath := path.Join(layerIDs[i], "layer.tar") layerFile, err := extractEmbeddedLayer(lb.file, layerTarPath, tmpLayerPath) if err != nil { return nil, nil, fmt.Errorf("error getting layer from file: %v", err) } defer layerFile.Close() log.Debug("Generating layer ACI...") aciPath, manifest, err := internal.GenerateACI(i, layerData, dockerURL, outputDir, layerFile, curPwl, compression) if err != nil { return nil, nil, fmt.Errorf("error generating ACI: %v", err) } aciLayerPaths = append(aciLayerPaths, aciPath) aciManifests = append(aciManifests, manifest) curPwl = manifest.PathWhitelist } return aciLayerPaths, aciManifests, nil }
func fixManifestLayers(manifest *v2Manifest) error { type imageV1 struct { ID string Parent string } imgs := make([]*imageV1, len(manifest.FSLayers)) for i := range manifest.FSLayers { img := &imageV1{} if err := json.Unmarshal([]byte(manifest.History[i].V1Compatibility), img); err != nil { return err } imgs[i] = img if err := common.ValidateLayerId(img.ID); err != nil { return err } } if imgs[len(imgs)-1].Parent != "" { return errors.New("Invalid parent ID in the base layer of the image.") } // check general duplicates to error instead of a deadlock idmap := make(map[string]struct{}) var lastID string for _, img := range imgs { // skip IDs that appear after each other, we handle those later if _, exists := idmap[img.ID]; img.ID != lastID && exists { return fmt.Errorf("ID %+v appears multiple times in manifest", img.ID) } lastID = img.ID idmap[lastID] = struct{}{} } // backwards loop so that we keep the remaining indexes after removing items for i := len(imgs) - 2; i >= 0; i-- { if imgs[i].ID == imgs[i+1].ID { // repeated ID. remove and continue manifest.FSLayers = append(manifest.FSLayers[:i], manifest.FSLayers[i+1:]...) manifest.History = append(manifest.History[:i], manifest.History[i+1:]...) } else if imgs[i].Parent != imgs[i+1].ID { return fmt.Errorf("Invalid parent ID. Expected %v, got %v.", imgs[i+1].ID, imgs[i].Parent) } } return nil }
func (rb *RepositoryBackend) buildACIV1(layerIDs []string, dockerURL *common.ParsedDockerURL, outputDir string, tmpBaseDir string, compression common.Compression) ([]string, []*schema.ImageManifest, error) { layerFiles := make([]*os.File, len(layerIDs)) layerDatas := make([]types.DockerImageData, len(layerIDs)) tmpParentDir, err := ioutil.TempDir(tmpBaseDir, "docker2aci-") if err != nil { return nil, nil, err } defer os.RemoveAll(tmpParentDir) var doneChannels []chan error for i, layerID := range layerIDs { if err := common.ValidateLayerId(layerID); err != nil { return nil, nil, err } doneChan := make(chan error) doneChannels = append(doneChannels, doneChan) // https://github.com/golang/go/wiki/CommonMistakes i := i // golang-- layerID := layerID go func() { tmpDir, err := ioutil.TempDir(tmpParentDir, "") if err != nil { doneChan <- fmt.Errorf("error creating dir: %v", err) return } j, size, err := rb.getJsonV1(layerID, rb.repoData.Endpoints[0], rb.repoData) if err != nil { doneChan <- fmt.Errorf("error getting image json: %v", err) return } layerDatas[i] = types.DockerImageData{} if err := json.Unmarshal(j, &layerDatas[i]); err != nil { doneChan <- fmt.Errorf("error unmarshaling layer data: %v", err) return } layerFiles[i], err = rb.getLayerV1(layerID, rb.repoData.Endpoints[0], rb.repoData, size, tmpDir) if err != nil { doneChan <- fmt.Errorf("error getting the remote layer: %v", err) return } doneChan <- nil }() } for _, doneChan := range doneChannels { err := <-doneChan if err != nil { return nil, nil, err } } var aciLayerPaths []string var aciManifests []*schema.ImageManifest var curPwl []string for i := len(layerIDs) - 1; i >= 0; i-- { rb.debug.Println("Generating layer ACI...") aciPath, manifest, err := internal.GenerateACI(i, layerDatas[i], dockerURL, outputDir, layerFiles[i], curPwl, compression, rb.debug) if err != nil { return nil, nil, fmt.Errorf("error generating ACI: %v", err) } aciLayerPaths = append(aciLayerPaths, aciPath) aciManifests = append(aciManifests, manifest) curPwl = manifest.PathWhitelist layerFiles[i].Close() } return aciLayerPaths, aciManifests, nil }
func (rb *RepositoryBackend) buildACIV21(layerIDs []string, dockerURL *common.ParsedDockerURL, outputDir string, tmpBaseDir string, compression common.Compression) ([]string, []*schema.ImageManifest, error) { layerFiles := make([]*os.File, len(layerIDs)) layerDatas := make([]types.DockerImageData, len(layerIDs)) tmpParentDir, err := ioutil.TempDir(tmpBaseDir, "docker2aci-") if err != nil { return nil, nil, err } defer os.RemoveAll(tmpParentDir) copier := progressutil.NewCopyProgressPrinter() var errChannels []chan error closers := make([]io.ReadCloser, len(layerIDs)) var wg sync.WaitGroup for i, layerID := range layerIDs { if err := common.ValidateLayerId(layerID); err != nil { return nil, nil, err } wg.Add(1) errChan := make(chan error, 1) errChannels = append(errChannels, errChan) // https://github.com/golang/go/wiki/CommonMistakes i := i // golang-- layerID := layerID go func() { defer wg.Done() manifest := rb.imageManifests[*dockerURL] layerIndex, ok := rb.layersIndex[layerID] if !ok { errChan <- fmt.Errorf("layer not found in manifest: %s", layerID) return } if len(manifest.History) <= layerIndex { errChan <- fmt.Errorf("history not found for layer %s", layerID) return } layerDatas[i] = types.DockerImageData{} if err := json.Unmarshal([]byte(manifest.History[layerIndex].V1Compatibility), &layerDatas[i]); err != nil { errChan <- fmt.Errorf("error unmarshaling layer data: %v", err) return } tmpDir, err := ioutil.TempDir(tmpParentDir, "") if err != nil { errChan <- fmt.Errorf("error creating dir: %v", err) return } layerFiles[i], closers[i], err = rb.getLayerV2(layerID, dockerURL, tmpDir, copier) if err != nil { errChan <- fmt.Errorf("error getting the remote layer: %v", err) return } errChan <- nil }() } // Need to wait for all of the readers to be added to the copier (which happens during rb.getLayerV2) wg.Wait() err = copier.PrintAndWait(os.Stderr, 500*time.Millisecond, nil) if err != nil { return nil, nil, err } for _, closer := range closers { if closer != nil { closer.Close() } } for _, errChan := range errChannels { err := <-errChan if err != nil { return nil, nil, err } } for _, layerFile := range layerFiles { err := layerFile.Sync() if err != nil { return nil, nil, err } } var aciLayerPaths []string var aciManifests []*schema.ImageManifest var curPwl []string for i := len(layerIDs) - 1; i >= 0; i-- { rb.debug.Println("Generating layer ACI...") aciPath, aciManifest, err := internal.GenerateACI(i, layerDatas[i], dockerURL, outputDir, layerFiles[i], curPwl, compression, rb.debug) if err != nil { return nil, nil, fmt.Errorf("error generating ACI: %v", err) } aciLayerPaths = append(aciLayerPaths, aciPath) aciManifests = append(aciManifests, aciManifest) curPwl = aciManifest.PathWhitelist layerFiles[i].Close() } return aciLayerPaths, aciManifests, nil }
func (rb *RepositoryBackend) buildACIV22(layerIDs []string, dockerURL *common.ParsedDockerURL, outputDir string, tmpBaseDir string, compression common.Compression) ([]string, []*schema.ImageManifest, error) { layerFiles := make([]*os.File, len(layerIDs)) tmpParentDir, err := ioutil.TempDir(tmpBaseDir, "docker2aci-") if err != nil { return nil, nil, err } defer os.RemoveAll(tmpParentDir) copier := progressutil.NewCopyProgressPrinter() resultChan := make(chan layer, len(layerIDs)) for i, layerID := range layerIDs { if err := common.ValidateLayerId(layerID); err != nil { return nil, nil, err } // https://github.com/golang/go/wiki/CommonMistakes i := i // golang-- layerID := layerID go func() { tmpDir, err := ioutil.TempDir(tmpParentDir, "") if err != nil { resultChan <- layer{ index: i, err: fmt.Errorf("error creating dir: %v", err), } return } layerFile, closer, err := rb.getLayerV2(layerID, dockerURL, tmpDir, copier) if err != nil { resultChan <- layer{ index: i, err: fmt.Errorf("error getting the remote layer: %v", err), } return } resultChan <- layer{ index: i, file: layerFile, closer: closer, err: nil, } }() } var errs []error for i := 0; i < len(layerIDs); i++ { res := <-resultChan if res.closer != nil { defer res.closer.Close() } if res.file != nil { defer res.file.Close() } if res.err != nil { errs = append(errs, res.err) } layerFiles[res.index] = res.file } if len(errs) > 0 { return nil, nil, errs[0] } err = copier.PrintAndWait(os.Stderr, 500*time.Millisecond, nil) if err != nil { return nil, nil, err } for _, layerFile := range layerFiles { err := layerFile.Sync() if err != nil { return nil, nil, err } } var aciLayerPaths []string var aciManifests []*schema.ImageManifest var curPwl []string var i int for i = 0; i < len(layerIDs)-1; i++ { rb.debug.Println("Generating layer ACI...") aciPath, aciManifest, err := internal.GenerateACI22LowerLayer(dockerURL, layerIDs[i], outputDir, layerFiles[i], curPwl, compression) if err != nil { return nil, nil, fmt.Errorf("error generating ACI: %v", err) } aciLayerPaths = append(aciLayerPaths, aciPath) aciManifests = append(aciManifests, aciManifest) curPwl = aciManifest.PathWhitelist } rb.debug.Println("Generating layer ACI...") aciPath, aciManifest, err := internal.GenerateACI22TopLayer(dockerURL, rb.imageConfigs[*dockerURL], layerIDs[i], outputDir, layerFiles[i], curPwl, compression, aciManifests, rb.debug) if err != nil { return nil, nil, fmt.Errorf("error generating ACI: %v", err) } aciLayerPaths = append(aciLayerPaths, aciPath) aciManifests = append(aciManifests, aciManifest) return aciLayerPaths, aciManifests, nil }