func convertPorts(dockerExposedPorts map[string]struct{}, dockerPortSpecs []string, debug log.Logger) ([]appctypes.Port, error) { ports := []appctypes.Port{} for ep := range dockerExposedPorts { appcPort, err := parseDockerPort(ep) if err != nil { return nil, err } ports = append(ports, *appcPort) } if dockerExposedPorts == nil && dockerPortSpecs != nil { debug.Println("warning: docker image uses deprecated PortSpecs field") for _, ep := range dockerPortSpecs { appcPort, err := parseDockerPort(ep) if err != nil { return nil, err } ports = append(ports, *appcPort) } } sort.Sort(appcPortSorter(ports)) return ports, nil }
func getParent(file *os.File, imgID string, debug log.Logger) (string, error) { var parent string _, err := file.Seek(0, 0) if err != nil { return "", fmt.Errorf("error seeking file: %v", err) } jsonPath := filepath.Join(imgID, "json") parentWalker := func(t *tarball.TarFile) error { if filepath.Clean(t.Name()) == jsonPath { jsonb, err := ioutil.ReadAll(t.TarStream) if err != nil { return fmt.Errorf("error reading layer json: %v", err) } var dockerData types.DockerImageData if err := json.Unmarshal(jsonb, &dockerData); err != nil { return fmt.Errorf("error unmarshaling layer data: %v", err) } parent = dockerData.Parent } return nil } tr := tar.NewReader(file) if err := tarball.Walk(*tr, parentWalker); err != nil { return "", err } debug.Printf("Layer %q depends on layer %q", imgID, parent) return parent, nil }
func extractEmbeddedLayer(file *os.File, layerTarPath string, outputPath string, info log.Logger) (*os.File, error) { info.Println("Extracting ", layerTarPath) _, err := file.Seek(0, 0) if err != nil { return nil, fmt.Errorf("error seeking file: %v", err) } var layerFile *os.File fileWalker := func(t *tarball.TarFile) error { if filepath.Clean(t.Name()) == layerTarPath { layerFile, err = os.Create(outputPath) if err != nil { return fmt.Errorf("error creating layer: %v", err) } _, err = io.Copy(layerFile, t.TarStream) if err != nil { return fmt.Errorf("error getting layer: %v", err) } } return nil } tr := tar.NewReader(file) if err := tarball.Walk(*tr, fileWalker); err != nil { return nil, err } if layerFile == nil { return nil, fmt.Errorf("file %q not found", layerTarPath) } return layerFile, nil }
// squashLayers receives a list of ACI layer file names ordered from base image // to application image and squashes them into one ACI func squashLayers(images []acirenderer.Image, aciRegistry acirenderer.ACIRegistry, parsedDockerURL common.ParsedDockerURL, outputDir string, compression common.Compression, debug log.Logger) (path string, err error) { debug.Println("Squashing layers...") debug.Println("Rendering ACI...") renderedACI, err := acirenderer.GetRenderedACIFromList(images, aciRegistry) if err != nil { return "", fmt.Errorf("error rendering squashed image: %v", err) } manifests, err := getManifests(renderedACI, aciRegistry) if err != nil { return "", fmt.Errorf("error getting manifests: %v", err) } squashedFilename := getSquashedFilename(parsedDockerURL) squashedImagePath := filepath.Join(outputDir, squashedFilename) squashedTempFile, err := ioutil.TempFile(outputDir, "docker2aci-squashedFile-") if err != nil { return "", err } defer func() { if err == nil { err = squashedTempFile.Close() } else { // remove temp file on error // we ignore its error to not mask the real error os.Remove(squashedTempFile.Name()) } }() debug.Println("Writing squashed ACI...") if err := writeSquashedImage(squashedTempFile, renderedACI, aciRegistry, manifests, compression); err != nil { return "", fmt.Errorf("error writing squashed image: %v", err) } debug.Println("Validating squashed ACI...") if err := internal.ValidateACI(squashedTempFile.Name()); err != nil { return "", fmt.Errorf("error validating image: %v", err) } if err := os.Rename(squashedTempFile.Name(), squashedImagePath); err != nil { return "", err } debug.Println("ACI squashed!") return squashedImagePath, nil }
// getAncestry computes an image ancestry, returning an ordered list // of dependencies starting from the topmost image to the base. // It checks for dependency loops via duplicate detection in the image // chain and errors out in such cases. func getAncestry(file *os.File, imgID string, debug log.Logger) ([]string, error) { var ancestry []string deps := make(map[string]bool) curImgID := imgID var err error for curImgID != "" { if deps[curImgID] { return nil, fmt.Errorf("dependency loop detected at image %q", curImgID) } deps[curImgID] = true ancestry = append(ancestry, curImgID) debug.Printf("Getting ancestry for layer %q", curImgID) curImgID, err = getParent(file, curImgID, debug) if err != nil { return nil, err } } return ancestry, nil }
func getImageID(file *os.File, dockerURL *common.ParsedDockerURL, name string, debug log.Logger) (string, []string, *common.ParsedDockerURL, error) { debug.Println("getting image id...") type tags map[string]string type apps map[string]tags _, err := file.Seek(0, 0) if err != nil { return "", nil, nil, fmt.Errorf("error seeking file: %v", err) } tag := "latest" if dockerURL != nil { tag = dockerURL.Tag } var imageID string var ancestry []string var appName string reposWalker := func(t *tarball.TarFile) error { clean := filepath.Clean(t.Name()) if clean == "repositories" { repob, err := ioutil.ReadAll(t.TarStream) if err != nil { return fmt.Errorf("error reading repositories file: %v", err) } var repositories apps if err := json.Unmarshal(repob, &repositories); err != nil { return fmt.Errorf("error unmarshaling repositories file") } if dockerURL == nil { n := len(repositories) switch { case n == 1: for key, _ := range repositories { appName = key } case n > 1: var appNames []string for key, _ := range repositories { appNames = append(appNames, key) } return &common.ErrSeveralImages{ Msg: "several images found", Images: appNames, } default: return fmt.Errorf("no images found") } } else { appName = dockerURL.ImageName } app, ok := repositories[appName] if !ok { return fmt.Errorf("app %q not found", appName) } _, ok = app[tag] if !ok { if len(app) == 1 { for key, _ := range app { tag = key } } else { return fmt.Errorf("tag %q not found", tag) } } if dockerURL == nil { dockerURL = &common.ParsedDockerURL{ OriginalName: "", IndexURL: "", Tag: tag, ImageName: appName, } } imageID = string(app[tag]) } if clean == "refs/"+tag { refb, err := ioutil.ReadAll(t.TarStream) if err != nil { return fmt.Errorf("error reading ref descriptor for tag %s: %v", tag, err) } if dockerURL == nil { dockerURL = &common.ParsedDockerURL{ IndexURL: "", Tag: tag, ImageName: name, } } var ref spec.Descriptor if err := json.Unmarshal(refb, &ref); err != nil { return fmt.Errorf("error unmarshaling ref descriptor for tag %s", tag) } imageID, ancestry, err = getDataFromManifest(file, ref.Digest) if err != nil { return err } return io.EOF } return nil } tr := tar.NewReader(file) if err := tarball.Walk(*tr, reposWalker); err != nil && err != io.EOF { return "", nil, nil, err } if imageID == "" { return "", nil, nil, fmt.Errorf("Could not find image") } return imageID, ancestry, dockerURL, nil }