diff --git a/lib/download_test.go b/lib/download_test.go index 519917eb..aeaf66b6 100644 --- a/lib/download_test.go +++ b/lib/download_test.go @@ -1,15 +1,26 @@ package lib import ( + "archive/zip" + "bytes" + "crypto" + "crypto/sha256" + "encoding/hex" "fmt" + "net/http" + "net/http/httptest" "net/url" "os" "path/filepath" "runtime" + "strings" "sync" "testing" "github.com/mitchellh/go-homedir" + "golang.org/x/crypto/openpgp" + "golang.org/x/crypto/openpgp/armor" + "golang.org/x/crypto/openpgp/packet" ) // TestDownloadFromURL_FileNameMatch : Check expected filename exist when downloaded @@ -79,7 +90,7 @@ func TestDownloadFromURL_FileNameMatch(t *testing.T) { }) } -// // TestDownloadFromURL_Valid : Test if https://releases.hashicorp.com/terraform/ is still valid +// TestDownloadFromURL_Valid : Test if https://releases.hashicorp.com/terraform/ is still valid func TestDownloadFromURL_Valid(t *testing.T) { hashiURL := "https://releases.hashicorp.com/terraform/" @@ -91,3 +102,114 @@ func TestDownloadFromURL_Valid(t *testing.T) { t.Logf("Valid URL from %v [expected]", url) } } + +// TestDownloadProductFromURL : Test DownloadProductFromURL +func TestDownloadProductFromURL(t *testing.T) { + + openpgpConfig := packet.Config{ + RSABits: 1024, + DefaultHash: crypto.SHA256, + DefaultCipher: packet.CipherAES256, + DefaultCompressionAlgo: packet.CompressionZLIB, + } + gpgKeyEntity, err := openpgp.NewEntity("TestProductSign", "Signing key for test product", "example@localhost.com", &openpgpConfig) + if err != nil { + fmt.Println(err) + return + } + gpgFingerprint := hex.EncodeToString(gpgKeyEntity.PrimaryKey.Fingerprint[:])[:8] + + zipFileBuffer := new(bytes.Buffer) + zipWriter := zip.NewWriter(zipFileBuffer) + + mainExecutableBytes := []byte("This is the main executable") + zipFileContentWriter, err := zipWriter.Create("myprod") + if err != nil { + t.Fatal(err) + } + zipFileContentWriter.Write(mainExecutableBytes) + zipWriter.Flush() + zipWriter.Close() + + var publicKeySerialiseBuffer bytes.Buffer + err = gpgKeyEntity.Serialize(&publicKeySerialiseBuffer) + if err != nil { + t.Fatal(err) + } + var publicKey bytes.Buffer + publicKeyWriter, err := armor.Encode(&publicKey, openpgp.PublicKeyType, nil) + if err != nil { + t.Fatal(err) + } + publicKeyWriter.Close() + + zipFileBytes := zipFileBuffer.Bytes() + + // Calculate SHA256 sum of ZIP file + sha256HashWriter := sha256.New() + sha256Hash := sha256HashWriter.Sum(zipFileBytes) + + // Create checksum file + checksumFileContent := string(sha256Hash) + " " + "my_product_download_2.1.0_linux_amd64.zip" + checksumFileReader := bytes.NewBuffer([]byte(checksumFileContent)) + + // Create signature of checksum file + var sigFile bytes.Buffer + err = openpgp.DetachSign(&sigFile, gpgKeyEntity, checksumFileReader, &openpgpConfig) + if err != nil { + t.Fatal(err) + } + + // Create mock server + mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch strings.TrimSpace(r.URL.Path) { + case "/testproduct/gpg-key.txt": + w.Header().Set("Content-Type", "text/html") + w.WriteHeader(http.StatusOK) + w.Write(publicKey.Bytes()) + case "/productdownload/2.1.0/my_product_download_2.1.0_linux_amd64.zip": + w.Header().Set("Content-Type", "application/zip") + w.WriteHeader(http.StatusOK) + w.Write(zipFileBytes) + case "/productdownload/2.1.0/my_product_download_2.1.0_SHA256SUMS": + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(http.StatusOK) + w.Write([]byte(checksumFileContent)) + case "/productdownload/2.1.0/my_product_download_2.1.0_SHA256SUMS." + gpgFingerprint + ".sig": + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(http.StatusOK) + w.Write(sigFile.Bytes()) + default: + http.NotFoundHandler().ServeHTTP(w, r) + } + })) + + // Create mock product + mockProduct := TerraformProduct{ + ProductDetails{ + ID: "myproduct", + Name: "Mock Product", + DefaultMirror: mockServer.URL + "/productdownload", + VersionPrefix: "myprod_", + ExecutableName: "myprod", + ArchivePrefix: "my_product_download_", + PublicKeyId: gpgFingerprint, + PublicKeyUrl: mockServer.URL + "/testproduct/gpg-key.txt", + }, + } + + // Create temp location + tempDir, err := os.MkdirTemp("", "addRecentVersion") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tempDir) + + zipFilePath, err := DownloadProductFromURL(mockProduct, tempDir, mockProduct.GetArtifactUrl(mockServer.URL+"/productdownload", "2.1.0"), "2.1.0", mockProduct.GetArchivePrefix(), "linux", "amd64") + if err != nil { + t.Fatal(err) + } + if expectedZipPath := ""; zipFilePath != expectedZipPath { + t.Errorf("Returned zipFile not expected path. Expected: %q, actual: %q", expectedZipPath, zipFilePath) + } +} diff --git a/lib/list_versions_test.go b/lib/list_versions_test.go index c142f5ab..e6f778a8 100644 --- a/lib/list_versions_test.go +++ b/lib/list_versions_test.go @@ -89,7 +89,7 @@ func compareLists(actual []string, expected []string) error { return nil } -func getMockServer() *httptest.Server { +func getMockListVersionServer() *httptest.Server { return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch strings.TrimSpace(r.URL.Path) { case "/hashicorp/": @@ -144,7 +144,7 @@ func TestGetVersionsFromBodyOpenTofu(t *testing.T) { // TestGetTFLatest : Test getTFLatest func TestGetTFLatest(t *testing.T) { - server := getMockServer() + server := getMockListVersionServer() defer server.Close() version, err := getTFLatest(fmt.Sprintf("%s/%s", server.URL, "hashicorp")) @@ -167,7 +167,7 @@ func TestGetTFLatestImplicit(t *testing.T) { } func testGetTFLatestImplicit(t *testing.T, version string, preRelease bool, expectedVersion string) { - server := getMockServer() + server := getMockListVersionServer() defer server.Close() version, err := getTFLatestImplicit(fmt.Sprintf("%s/%s", server.URL, "hashicorp"), preRelease, version) @@ -181,7 +181,7 @@ func testGetTFLatestImplicit(t *testing.T, version string, preRelease bool, expe // TestGetTFURLBody : Test getTFURLBody method func TestGetTFURLBody(t *testing.T) { - server := getMockServer() + server := getMockListVersionServer() defer server.Close() body, err := getTFURLBody(fmt.Sprintf("%s/%s", server.URL, "hashicorp")) diff --git a/lib/products.go b/lib/products.go index c7d4b08f..3b6270b5 100644 --- a/lib/products.go +++ b/lib/products.go @@ -58,7 +58,7 @@ func (p TerraformProduct) GetArchivePrefix() string { return p.ArchivePrefix } func (p TerraformProduct) GetArtifactUrl(mirrorURL string, version string) string { - return fmt.Sprintf("%s%s", mirrorURL, version) + return fmt.Sprintf("%s/%s", mirrorURL, version) } func (p TerraformProduct) GetPublicKeyId() string { return p.PublicKeyId