// FIXME: mostly copy/paste from github.com/appc/spec/actool/validate.go func DecompressingReader(rd io.Reader) (io.Reader, error) { brd := bufio.NewReaderSize(rd, 1024) header, err := brd.Peek(768) if err != nil { return nil, errors.Trace(err) } typ, err := aci.DetectFileType(bytes.NewReader(header)) if err != nil { return nil, errors.Trace(err) } var r io.Reader switch typ { case aci.TypeGzip: r, err = gzip.NewReader(brd) if err != nil { return nil, errors.Trace(err) } case aci.TypeBzip2: r = bzip2.NewReader(brd) case aci.TypeXz: r = aci.XzReader(brd) case aci.TypeTar: r = brd case aci.TypeUnknown: return nil, errors.New("unknown filetype") default: panic(fmt.Sprintf("bad type returned from DetectFileType: %v", typ)) } return r, nil }
func maybeDecompress(rs io.ReadSeeker) (io.Reader, error) { // TODO(jonboulle): this is a bit redundant with detectValType typ, err := aci.DetectFileType(rs) if err != nil { return nil, err } if _, err := rs.Seek(0, 0); err != nil { return nil, err } var r io.Reader switch typ { case aci.TypeGzip: r, err = gzip.NewReader(rs) if err != nil { return nil, fmt.Errorf("error reading gzip: %v", err) } case aci.TypeBzip2: r = bzip2.NewReader(rs) case aci.TypeXz: r = aci.XzReader(rs) case aci.TypeTar: r = rs case aci.TypeUnknown: return nil, errors.New("unknown filetype") default: // should never happen panic(fmt.Sprintf("bad type returned from DetectFileType: %v", typ)) } return r, nil }
// Need to uncompress the file to be able to generate the Image ID func (r Registry) uncompress() error { acifile, err := os.Open(r.tmppath()) if err != nil { return err } defer acifile.Close() typ, err := aci.DetectFileType(acifile) if err != nil { return err } // In case DetectFileType changed the cursor _, err = acifile.Seek(0, 0) if err != nil { return err } var in io.Reader switch typ { case aci.TypeGzip: in, err = gzip.NewReader(acifile) if err != nil { return err } case aci.TypeBzip2: in = bzip2.NewReader(acifile) case aci.TypeXz: in, err = xz.NewReader(acifile, 0) if err != nil { return err } case aci.TypeTar: in = acifile case aci.TypeText: return fmt.Errorf("downloaded ACI is text, not a tarball") case aci.TypeUnknown: return fmt.Errorf("downloaded ACI is of an unknown type") } out, err := os.OpenFile(r.tmpuncompressedpath(), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) if err != nil { return err } defer out.Close() _, err = io.Copy(out, in) if err != nil { return fmt.Errorf("error copying: %v", err) } err = out.Sync() if err != nil { return fmt.Errorf("error writing: %v", err) } return nil }
func detectValType(file *os.File) (string, error) { typ, err := aci.DetectFileType(file) if err != nil { return "", err } if _, err := file.Seek(0, 0); err != nil { return "", err } switch typ { case aci.TypeXz, aci.TypeGzip, aci.TypeBzip2, aci.TypeTar: return typeAppImage, nil case aci.TypeText: return typeManifest, nil default: return "", nil } }
// WriteACI takes an ACI encapsulated in an io.Reader, decompresses it if // necessary, and then stores it in the store under a key based on the image ID // (i.e. the hash of the uncompressed ACI) // latest defines if the aci has to be marked as the latest. For example an ACI // discovered without asking for a specific version (latest pattern). func (s Store) WriteACI(r io.Reader, latest bool) (string, error) { // Peek at the first 512 bytes of the reader to detect filetype br := bufio.NewReaderSize(r, 32768) hd, err := br.Peek(512) switch err { case nil: case io.EOF: // We may have still peeked enough to guess some types, so fall through default: return "", fmt.Errorf("error reading image header: %v", err) } typ, err := aci.DetectFileType(bytes.NewBuffer(hd)) if err != nil { return "", fmt.Errorf("error detecting image type: %v", err) } dr, err := decompress(br, typ) if err != nil { return "", fmt.Errorf("error decompressing image: %v", err) } // Write the decompressed image (tar) to a temporary file on disk, and // tee so we can generate the hash h := sha512.New() tr := io.TeeReader(dr, h) fh, err := s.TmpFile() if err != nil { return "", fmt.Errorf("error creating image: %v", err) } if _, err := io.Copy(fh, tr); err != nil { return "", fmt.Errorf("error copying image: %v", err) } im, err := aci.ManifestFromImage(fh) if err != nil { return "", fmt.Errorf("error extracting image manifest: %v", err) } if err := fh.Close(); err != nil { return "", fmt.Errorf("error closing image: %v", err) } // Import the uncompressed image into the store at the real key key := s.HashToKey(h) keyLock, err := lock.ExclusiveKeyLock(s.imageLockDir, key) if err != nil { return "", fmt.Errorf("error locking image: %v", err) } defer keyLock.Close() if err = s.stores[blobType].Import(fh.Name(), key, true); err != nil { return "", fmt.Errorf("error importing image: %v", err) } // Save the imagemanifest using the same key used for the image imj, err := json.Marshal(im) if err != nil { return "", fmt.Errorf("error marshalling image manifest: %v", err) } if err = s.stores[imageManifestType].Write(key, imj); err != nil { return "", fmt.Errorf("error importing image manifest: %v", err) } // Save aciinfo if err = s.db.Do(func(tx *sql.Tx) error { aciinfo := &ACIInfo{ BlobKey: key, AppName: im.Name.String(), ImportTime: time.Now(), Latest: latest, } return WriteACIInfo(tx, aciinfo) }); err != nil { return "", fmt.Errorf("error writing ACI Info: %v", err) } // The treestore for this ACI is not written here as ACIs downloaded as // dependencies of another ACI will be exploded also if never directly used. // Users of treestore should call s.RenderTreeStore before using it. return key, nil }