-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add DataDetector to support ACI and other layout format #49
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
b1775ed
add data detector to support ACI and other format in the future
liangchenye d402ae8
use imageFormat in params for consistence
liangchenye 354c4b3
always trim './'
liangchenye 8b649af
detect bzip2/xz; add test data
liangchenye 41509cc
add imageFormt to API.md; add xz to Dockerfile; fix bugs
liangchenye 4b11491
add ImageFormat to worker_test and programs under contrib
liangchenye File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,10 +18,12 @@ import ( | |
"archive/tar" | ||
"bufio" | ||
"bytes" | ||
"compress/bzip2" | ||
"compress/gzip" | ||
"errors" | ||
"io" | ||
"io/ioutil" | ||
"os/exec" | ||
"strings" | ||
) | ||
|
||
|
@@ -32,19 +34,75 @@ var ( | |
// ErrExtractedFileTooBig occurs when a file to extract is too big. | ||
ErrExtractedFileTooBig = errors.New("utils: could not extract one or more files from the archive: file too big") | ||
|
||
gzipHeader = []byte{0x1f, 0x8b} | ||
readLen = 6 // max bytes to sniff | ||
|
||
gzipHeader = []byte{0x1f, 0x8b} | ||
bzip2Header = []byte{0x42, 0x5a, 0x68} | ||
xzHeader = []byte{0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00} | ||
) | ||
|
||
// XzReader is an io.ReadCloser which decompresses xz compressed data. | ||
type XzReader struct { | ||
io.ReadCloser | ||
cmd *exec.Cmd | ||
closech chan error | ||
} | ||
|
||
// NewXzReader shells out to a command line xz executable (if | ||
// available) to decompress the given io.Reader using the xz | ||
// compression format and returns an *XzReader. | ||
// It is the caller's responsibility to call Close on the XzReader when done. | ||
func NewXzReader(r io.Reader) (*XzReader, error) { | ||
rpipe, wpipe := io.Pipe() | ||
ex, err := exec.LookPath("xz") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
if err != nil { | ||
return nil, err | ||
} | ||
cmd := exec.Command(ex, "--decompress", "--stdout") | ||
|
||
closech := make(chan error) | ||
|
||
cmd.Stdin = r | ||
cmd.Stdout = wpipe | ||
|
||
go func() { | ||
err := cmd.Run() | ||
wpipe.CloseWithError(err) | ||
closech <- err | ||
}() | ||
|
||
return &XzReader{rpipe, cmd, closech}, nil | ||
} | ||
|
||
func (r *XzReader) Close() error { | ||
r.ReadCloser.Close() | ||
r.cmd.Process.Kill() | ||
return <-r.closech | ||
} | ||
|
||
// TarReadCloser embeds a *tar.Reader and the related io.Closer | ||
// It is the caller's responsibility to call Close on TarReadCloser when | ||
// done. | ||
type TarReadCloser struct { | ||
*tar.Reader | ||
io.Closer | ||
} | ||
|
||
func (r *TarReadCloser) Close() error { | ||
return r.Closer.Close() | ||
} | ||
|
||
// SelectivelyExtractArchive extracts the specified files and folders | ||
// from targz data read from the given reader and store them in a map indexed by file paths | ||
func SelectivelyExtractArchive(r io.Reader, toExtract []string, maxFileSize int64) (map[string][]byte, error) { | ||
func SelectivelyExtractArchive(r io.Reader, prefix string, toExtract []string, maxFileSize int64) (map[string][]byte, error) { | ||
data := make(map[string][]byte) | ||
|
||
// Create a tar or tar/tar-gzip reader | ||
// Create a tar or tar/tar-gzip/tar-bzip2/tar-xz reader | ||
tr, err := getTarReader(r) | ||
if err != nil { | ||
return data, ErrCouldNotExtract | ||
} | ||
defer tr.Close() | ||
|
||
// For each element in the archive | ||
for { | ||
|
@@ -59,6 +117,9 @@ func SelectivelyExtractArchive(r io.Reader, toExtract []string, maxFileSize int6 | |
// Get element filename | ||
filename := hdr.Name | ||
filename = strings.TrimPrefix(filename, "./") | ||
if prefix != "" { | ||
filename = strings.TrimPrefix(filename, prefix) | ||
} | ||
|
||
// Determine if we should extract the element | ||
toBeExtracted := false | ||
|
@@ -86,22 +147,35 @@ func SelectivelyExtractArchive(r io.Reader, toExtract []string, maxFileSize int6 | |
return data, nil | ||
} | ||
|
||
// getTarReader returns a tar.Reader associated with the specified io.Reader, | ||
// optionally backed by a gzip.Reader if gzip compression is detected. | ||
// getTarReader returns a TarReaderCloser associated with the specified io.Reader. | ||
// | ||
// Gzip detection is done by using the magic numbers defined in the RFC1952 : | ||
// the first two bytes should be 0x1f and 0x8b.. | ||
func getTarReader(r io.Reader) (*tar.Reader, error) { | ||
// Gzip/Bzip2/XZ detection is done by using the magic numbers: | ||
// Gzip: the first two bytes should be 0x1f and 0x8b. Defined in the RFC1952. | ||
// Bzip2: the first three bytes should be 0x42, 0x5a and 0x68. No RFC. | ||
// XZ: the first three bytes should be 0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00. No RFC. | ||
func getTarReader(r io.Reader) (*TarReadCloser, error) { | ||
br := bufio.NewReader(r) | ||
header, err := br.Peek(2) | ||
|
||
if err == nil && bytes.Equal(header, gzipHeader) { | ||
gr, err := gzip.NewReader(br) | ||
if err != nil { | ||
return nil, err | ||
header, err := br.Peek(readLen) | ||
if err == nil { | ||
switch { | ||
case bytes.HasPrefix(header, gzipHeader): | ||
gr, err := gzip.NewReader(br) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &TarReadCloser{tar.NewReader(gr), gr}, nil | ||
case bytes.HasPrefix(header, bzip2Header): | ||
bzip2r := ioutil.NopCloser(bzip2.NewReader(br)) | ||
return &TarReadCloser{tar.NewReader(bzip2r), bzip2r}, nil | ||
case bytes.HasPrefix(header, xzHeader): | ||
xzr, err := NewXzReader(br) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &TarReadCloser{tar.NewReader(xzr), xzr}, nil | ||
} | ||
return tar.NewReader(gr), nil | ||
} | ||
|
||
return tar.NewReader(br), nil | ||
dr := ioutil.NopCloser(br) | ||
return &TarReadCloser{tar.NewReader(dr), dr}, nil | ||
} |
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
// Copyright 2015 clair authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
// Package detectors exposes functions to register and use container | ||
// information extractors. | ||
package detectors | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"os" | ||
"strings" | ||
"sync" | ||
|
||
cerrors "github.com/coreos/clair/utils/errors" | ||
) | ||
|
||
// The DataDetector interface defines a way to detect the required data from input path | ||
type DataDetector interface { | ||
//Support check if the input path and format are supported by the underling detector | ||
Supported(path string, format string) bool | ||
// Detect detects the required data from input path | ||
Detect(layerReader io.ReadCloser, toExtract []string, maxFileSize int64) (data map[string][]byte, err error) | ||
} | ||
|
||
var ( | ||
dataDetectorsLock sync.Mutex | ||
dataDetectors = make(map[string]DataDetector) | ||
) | ||
|
||
// RegisterDataDetector provides a way to dynamically register an implementation of a | ||
// DataDetector. | ||
// | ||
// If RegisterDataDetector is called twice with the same name if DataDetector is nil, | ||
// or if the name is blank, it panics. | ||
func RegisterDataDetector(name string, f DataDetector) { | ||
if name == "" { | ||
panic("Could not register a DataDetector with an empty name") | ||
} | ||
if f == nil { | ||
panic("Could not register a nil DataDetector") | ||
} | ||
|
||
dataDetectorsLock.Lock() | ||
defer dataDetectorsLock.Unlock() | ||
|
||
if _, alreadyExists := dataDetectors[name]; alreadyExists { | ||
panic(fmt.Sprintf("Detector '%s' is already registered", name)) | ||
} | ||
dataDetectors[name] = f | ||
} | ||
|
||
// DetectData finds the Data of the layer by using every registered DataDetector | ||
func DetectData(path string, format string, toExtract []string, maxFileSize int64) (data map[string][]byte, err error) { | ||
var layerReader io.ReadCloser | ||
if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") { | ||
r, err := http.Get(path) | ||
if err != nil { | ||
return nil, cerrors.ErrCouldNotDownload | ||
} | ||
layerReader = r.Body | ||
} else { | ||
layerReader, err = os.Open(path) | ||
if err != nil { | ||
return nil, cerrors.ErrNotFound | ||
} | ||
} | ||
defer layerReader.Close() | ||
|
||
for _, detector := range dataDetectors { | ||
if detector.Supported(path, format) { | ||
if data, err = detector.Detect(layerReader, toExtract, maxFileSize); err == nil { | ||
return data, nil | ||
} | ||
} | ||
} | ||
|
||
return nil, nil | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
API doc about layer insertion should be updated as a new parameter appeared