Signed-off-by: Suleiman Dibirov <idsulik@gmail.com>
pull/31405/head
Suleiman Dibirov 2 months ago
parent 8810b98890
commit e2cbf044cc

@ -68,6 +68,10 @@ func NewExtractor(source string) (Extractor, error) {
return extractor, nil
}
}
if strings.HasPrefix(source, "http") && isGzipArchiveFromURL(source) {
return &TarGzExtractor{}, nil
}
return nil, fmt.Errorf("no extractor implemented yet for %s", source)
}

@ -0,0 +1,50 @@
/*
Copyright The Helm 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 installer
import "net/http"
// isGzipArchive checks if data represents a gzip archive by checking the magic bytes
func isGzipArchive(data []byte) bool {
return len(data) >= 2 && data[0] == 0x1f && data[1] == 0x8b
}
// isGzipArchiveFromURL checks if a URL points to a gzip archive by reading the magic bytes
func isGzipArchiveFromURL(url string) bool {
// Make a GET request to read the first few bytes
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return false
}
// Request only the first 512 bytes to check magic bytes
req.Header.Set("Range", "bytes=0-511")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return false
}
defer resp.Body.Close()
// Read the first few bytes
buf := make([]byte, 2)
n, err := resp.Body.Read(buf)
if err != nil || n < 2 {
return false
}
return isGzipArchive(buf)
}

@ -174,49 +174,44 @@ func isLocalReference(source string) bool {
// It works by checking whether the source looks like a URL and, if it does, running a
// HEAD operation to see if the remote resource is a file that we understand.
func isRemoteHTTPArchive(source string) bool {
if !isHTTPURL(source) {
return false
}
contentType, err := getRemoteContentType(source)
if err != nil {
return false
}
// Handle octet-stream specially by checking file extension
if contentType == "application/octet-stream" {
if strings.HasPrefix(source, "http://") || strings.HasPrefix(source, "https://") {
// First, check if the URL ends with a known archive suffix
// This is more reliable than content-type detection
for suffix := range Extractors {
if strings.HasSuffix(source, suffix) {
return true
}
}
return false
}
// Check if we have an extractor for this media type
if suffix, ok := mediaTypeToExtension(contentType); ok {
_, hasExtractor := Extractors[suffix]
return hasExtractor
}
// If no suffix match, try HEAD request to check content type
res, err := http.Head(source)
if err != nil {
// If we get an error at the network layer, we can't install it. So
// we return false.
return false
}
return false
}
// Next, we look for the content type or content disposition headers to see
// if they have matching extractors.
contentType := res.Header.Get("content-type")
foundSuffix, ok := mediaTypeToExtension(contentType)
if !ok {
if contentType == "application/octet-stream" && isGzipArchiveFromURL(source) {
// For generic binary content, try to detect the actual file type
// by reading the first few bytes (magic bytes)
return true
}
// isHTTPURL checks if the source is an HTTP or HTTPS URL
func isHTTPURL(source string) bool {
return strings.HasPrefix(source, "http://") || strings.HasPrefix(source, "https://")
}
return false
}
// getRemoteContentType performs a HEAD request and returns the content-type
func getRemoteContentType(url string) (string, error) {
res, err := http.Head(url)
if err != nil {
return "", err
for suffix := range Extractors {
if strings.HasSuffix(foundSuffix, suffix) {
return true
}
}
}
defer res.Body.Close()
return res.Header.Get("content-type"), nil
return false
}
// isPlugin checks if the directory contains a plugin.yaml file.

@ -128,7 +128,7 @@ func (i *OCIInstaller) Install() error {
}
// Check if this is a gzip compressed file
if len(i.pluginData) < 2 || i.pluginData[0] != 0x1f || i.pluginData[1] != 0x8b {
if !isGzipArchive(i.pluginData) {
return fmt.Errorf("plugin data is not a gzip compressed archive")
}

@ -792,7 +792,7 @@ func TestOCIInstaller_Install_ValidationErrors(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Test the gzip validation logic that's used in the Install method
if len(tt.layerData) < 2 || tt.layerData[0] != 0x1f || tt.layerData[1] != 0x8b {
if !isGzipArchive(tt.layerData) {
// This matches the validation in the Install method
if !tt.expectError {
t.Error("expected valid gzip data")

Loading…
Cancel
Save