| 22 | ) |
| 23 | |
| 24 | func extractFromImage(fs billy.Filesystem, directory, reference string) error { |
| 25 | ref, err := name.ParseReference(reference) |
| 26 | if err != nil { |
| 27 | return fmt.Errorf("parse feature ref %s: %w", reference, err) |
| 28 | } |
| 29 | image, err := remote.Image(ref, remote.WithAuthFromKeychain(creds.GetKeychain())) |
| 30 | if err != nil { |
| 31 | return fmt.Errorf("fetch feature image %s: %w", reference, err) |
| 32 | } |
| 33 | manifest, err := image.Manifest() |
| 34 | if err != nil { |
| 35 | return fmt.Errorf("fetch feature manifest %s: %w", reference, err) |
| 36 | } |
| 37 | |
| 38 | var tarLayer *tar.Reader |
| 39 | for _, manifestLayer := range manifest.Layers { |
| 40 | if manifestLayer.MediaType != TarLayerMediaType { |
| 41 | continue |
| 42 | } |
| 43 | layer, err := image.LayerByDigest(manifestLayer.Digest) |
| 44 | if err != nil { |
| 45 | return fmt.Errorf("fetch feature layer %s: %w", reference, err) |
| 46 | } |
| 47 | layerReader, err := layer.Uncompressed() |
| 48 | if err != nil { |
| 49 | return fmt.Errorf("uncompress feature layer %s: %w", reference, err) |
| 50 | } |
| 51 | tarLayer = tar.NewReader(layerReader) |
| 52 | break |
| 53 | } |
| 54 | if tarLayer == nil { |
| 55 | return fmt.Errorf("no tar layer found with media type %q: are you sure this is a devcontainer feature?", TarLayerMediaType) |
| 56 | } |
| 57 | |
| 58 | for { |
| 59 | header, err := tarLayer.Next() |
| 60 | if err == io.EOF { |
| 61 | break |
| 62 | } |
| 63 | if err != nil { |
| 64 | return fmt.Errorf("read feature layer %s: %w", reference, err) |
| 65 | } |
| 66 | path := filepath.Join(directory, header.Name) |
| 67 | switch header.Typeflag { |
| 68 | case tar.TypeDir: |
| 69 | err = fs.MkdirAll(path, 0o755) |
| 70 | if err != nil { |
| 71 | return fmt.Errorf("mkdir %s: %w", path, err) |
| 72 | } |
| 73 | case tar.TypeReg: |
| 74 | outFile, err := fs.Create(path) |
| 75 | if err != nil { |
| 76 | return fmt.Errorf("create %s: %w", path, err) |
| 77 | } |
| 78 | _, err = io.Copy(outFile, tarLayer) |
| 79 | if err != nil { |
| 80 | return fmt.Errorf("copy %s: %w", path, err) |
| 81 | } |