From e5395c7a2152f1bcff02b9bb2d2080dfec35011e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lio=E6=9D=8E=E6=AD=90?= Date: Fri, 10 Nov 2023 11:05:36 -0800 Subject: [PATCH] feat: support https URLs for digest-file (#2811) This feature allows one to specify an https URL for any of the digest-file options, resulting in an HTTP PUT to the provided URL. This could for example be a (pre-signed) URL to S3 or GCS. Currently the final digest is only written to the local filesystem, which disappears and is not accessible when Kaniko is run in a managed container service like AWS ECS. By supporting https a single implementation supports all storage services, without the need for special code for S3, GCS, etc.. --- pkg/executor/push.go | 12 ++++++++++++ pkg/executor/push_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/pkg/executor/push.go b/pkg/executor/push.go index 45b663bc0..a953d0fef 100644 --- a/pkg/executor/push.go +++ b/pkg/executor/push.go @@ -17,6 +17,7 @@ limitations under the License. package executor import ( + "bytes" "encoding/json" "fmt" "io/ioutil" @@ -136,6 +137,17 @@ func getDigest(image v1.Image) ([]byte, error) { } func writeDigestFile(path string, digestByteArray []byte) error { + if strings.HasPrefix(path, "https://") { + // Do a HTTP PUT to the URL; this could be a pre-signed URL to S3 or GCS or Azure + req, err := http.NewRequest("PUT", path, bytes.NewReader(digestByteArray)) //nolint:noctx + if err != nil { + return err + } + req.Header.Set("Content-Type", "text/plain") + _, err = http.DefaultClient.Do(req) + return err + } + parentDir := filepath.Dir(path) if _, err := os.Stat(parentDir); os.IsNotExist(err) { if err := os.MkdirAll(parentDir, 0700); err != nil { diff --git a/pkg/executor/push_test.go b/pkg/executor/push_test.go index d5ad03684..d1423de6c 100644 --- a/pkg/executor/push_test.go +++ b/pkg/executor/push_test.go @@ -19,8 +19,10 @@ package executor import ( "bytes" "fmt" + "io" "io/ioutil" "net/http" + "net/http/httptest" "os" "path/filepath" "testing" @@ -475,4 +477,32 @@ func TestWriteDigestFile(t *testing.T) { t.Errorf("expected file to be written successfully, but got error: %v", err) } }) + + t.Run("https PUT OK", func(t *testing.T) { + var uploadedContent []byte + + // Start a test server that checks the PUT request. + server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPut { + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + uploadedContent, _ = io.ReadAll(r.Body) + w.WriteHeader(http.StatusNoContent) + })) + defer server.Close() + + // Temporarily replace the default client with the test server client to avoid TLS verification errors. + oldClient := http.DefaultClient + defer func() { http.DefaultClient = oldClient }() + http.DefaultClient = server.Client() + + err := writeDigestFile(server.URL+"/df?sig=1234", []byte("test")) + if err != nil { + t.Fatalf("expected file to be written successfully, but got error: %v", err) + } + if string(uploadedContent) != "test" { + t.Errorf("expected uploaded content to be 'test', but got '%s'", uploadedContent) + } + }) }