mirror of https://github.com/hashicorp/packer
changes for pulling binary from releases.hashicorp.com (#13431)
* changes for pulling binary from releases.hashicorp.com * cahnges for getting release from release official site * cahnges for getting release from release official site * unit test cases * unit test cases * unit test coverage * changes for getter releases.hashicorp.com * lint fix * lint fix * lint fix * manifest.json related changes * manifest.json related changes * manifest.json related changes * manifest.json related changes * github getter test cases * added test cases for getters * added test cases for getters * added test cases for getters * added test cases for getters * added test cases for getters * added test cases for plugins getter * unit test cases for getting release from official site * description to the methodsanshul/changelog_1.14.0
parent
e45ff9b093
commit
a234126747
@ -0,0 +1,192 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
plugingetter "github.com/hashicorp/packer/packer/plugin-getter"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
entry *plugingetter.ChecksumFileEntry
|
||||
binVersion string
|
||||
protocolVersion string
|
||||
os string
|
||||
arch string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid format parses",
|
||||
entry: &plugingetter.ChecksumFileEntry{
|
||||
Filename: "packer-plugin-v0.2.12_x5.0_freebsd_amd64.zip",
|
||||
},
|
||||
binVersion: "v0.2.12",
|
||||
protocolVersion: "x5.0",
|
||||
os: "freebsd",
|
||||
arch: "amd64",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "malformed filename returns error",
|
||||
entry: &plugingetter.ChecksumFileEntry{
|
||||
Filename: "packer-plugin-v0.2.12_x5.0.zip",
|
||||
},
|
||||
binVersion: "v0.2.12",
|
||||
protocolVersion: "x5.0",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
req := &plugingetter.Requirement{}
|
||||
|
||||
getter := &Getter{}
|
||||
err := getter.Init(req, tt.entry)
|
||||
|
||||
if err != nil && !tt.wantErr {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
if err == nil && tt.wantErr {
|
||||
t.Fatal("expected error but got nil")
|
||||
}
|
||||
|
||||
if !tt.wantErr && (tt.entry.BinVersion != "v0.2.12" || tt.entry.ProtVersion != "x5.0" || tt.entry.Os != "freebsd" || tt.entry.Arch != "amd64") {
|
||||
t.Fatalf("unexpected parsed values: %+v", tt.entry)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
option plugingetter.GetOptions
|
||||
installOpts plugingetter.BinaryInstallationOptions
|
||||
entry *plugingetter.ChecksumFileEntry
|
||||
version string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid entry",
|
||||
installOpts: plugingetter.BinaryInstallationOptions{
|
||||
OS: "linux",
|
||||
ARCH: "amd64",
|
||||
},
|
||||
entry: &plugingetter.ChecksumFileEntry{
|
||||
BinVersion: "v1.2.3",
|
||||
Os: "linux",
|
||||
Arch: "amd64",
|
||||
ProtVersion: "x5.0",
|
||||
},
|
||||
version: "1.2.3",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid bin version",
|
||||
installOpts: plugingetter.BinaryInstallationOptions{
|
||||
OS: "linux",
|
||||
ARCH: "amd64",
|
||||
},
|
||||
entry: &plugingetter.ChecksumFileEntry{
|
||||
BinVersion: "v1.2.3",
|
||||
Os: "linux",
|
||||
Arch: "amd64",
|
||||
ProtVersion: "x5.0",
|
||||
},
|
||||
version: "1.2.4",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "wrong OS",
|
||||
installOpts: plugingetter.BinaryInstallationOptions{
|
||||
OS: "linux",
|
||||
ARCH: "amd64",
|
||||
},
|
||||
entry: &plugingetter.ChecksumFileEntry{
|
||||
BinVersion: "v1.2.3",
|
||||
Os: "darwin",
|
||||
Arch: "amd64",
|
||||
ProtVersion: "x5.0",
|
||||
},
|
||||
version: "1.2.3",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "wrong Arch",
|
||||
installOpts: plugingetter.BinaryInstallationOptions{
|
||||
OS: "linux",
|
||||
ARCH: "amd64",
|
||||
},
|
||||
entry: &plugingetter.ChecksumFileEntry{
|
||||
BinVersion: "v1.2.3",
|
||||
Os: "linux",
|
||||
Arch: "arm64",
|
||||
ProtVersion: "x5.0",
|
||||
},
|
||||
version: "1.2.3",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid API major version",
|
||||
installOpts: plugingetter.BinaryInstallationOptions{
|
||||
OS: "linux",
|
||||
ARCH: "amd64",
|
||||
APIVersionMajor: "5",
|
||||
APIVersionMinor: "0",
|
||||
},
|
||||
entry: &plugingetter.ChecksumFileEntry{
|
||||
BinVersion: "v1.2.3",
|
||||
Os: "linux",
|
||||
Arch: "amd64",
|
||||
ProtVersion: "x4.0",
|
||||
},
|
||||
version: "1.2.3",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid API minor version",
|
||||
installOpts: plugingetter.BinaryInstallationOptions{
|
||||
OS: "linux",
|
||||
ARCH: "amd64",
|
||||
APIVersionMajor: "5",
|
||||
APIVersionMinor: "0",
|
||||
},
|
||||
entry: &plugingetter.ChecksumFileEntry{
|
||||
BinVersion: "v1.2.3",
|
||||
Os: "linux",
|
||||
Arch: "amd64",
|
||||
ProtVersion: "x5.4",
|
||||
},
|
||||
version: "1.2.3",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
getter := &Getter{}
|
||||
err := getter.Validate(plugingetter.GetOptions{}, tt.version, tt.installOpts, tt.entry)
|
||||
|
||||
if err != nil && !tt.wantErr {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
if err == nil && tt.wantErr {
|
||||
t.Fatal("expected error but got nil")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpectedFileName(t *testing.T) {
|
||||
getter := &Getter{}
|
||||
pr := plugingetter.Requirement{}
|
||||
fileName := getter.ExpectedFileName(&pr, "1.2.3", &plugingetter.ChecksumFileEntry{}, "packer-plugin-docker_v1.2.3_x5.0_linux_amd64.zip")
|
||||
assert.Equal(t, "packer-plugin-docker_v1.2.3_x5.0_linux_amd64.zip", fileName)
|
||||
}
|
||||
@ -0,0 +1,211 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package release
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
plugingetter "github.com/hashicorp/packer/packer/plugin-getter"
|
||||
gh "github.com/hashicorp/packer/packer/plugin-getter/github"
|
||||
)
|
||||
|
||||
const officialReleaseURL = "https://releases.hashicorp.com/"
|
||||
|
||||
type Getter struct {
|
||||
APIMajor string
|
||||
APIMinor string
|
||||
HttpClient *http.Client
|
||||
Name string
|
||||
}
|
||||
|
||||
var _ plugingetter.Getter = &Getter{}
|
||||
|
||||
func transformZipStream() func(in io.ReadCloser) (io.ReadCloser, error) {
|
||||
return func(in io.ReadCloser) (io.ReadCloser, error) {
|
||||
defer in.Close()
|
||||
buf := new(bytes.Buffer)
|
||||
_, err := io.Copy(buf, in)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return io.NopCloser(buf), nil
|
||||
}
|
||||
}
|
||||
|
||||
// transformReleasesVersionStream get a stream from github tags and transforms it into
|
||||
// something Packer wants, namely a json list of Release.
|
||||
func transformReleasesVersionStream(in io.ReadCloser) (io.ReadCloser, error) {
|
||||
if in == nil {
|
||||
return nil, fmt.Errorf("transformReleasesVersionStream got nil body")
|
||||
}
|
||||
defer in.Close()
|
||||
dec := json.NewDecoder(in)
|
||||
|
||||
var m gh.PluginMetadata
|
||||
if err := dec.Decode(&m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var out []plugingetter.Release
|
||||
for _, m := range m.Versions {
|
||||
out = append(out, plugingetter.Release{
|
||||
Version: "v" + m.Version,
|
||||
})
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
if err := json.NewEncoder(buf).Encode(out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return io.NopCloser(buf), nil
|
||||
}
|
||||
|
||||
func (g *Getter) Get(what string, opts plugingetter.GetOptions) (io.ReadCloser, error) {
|
||||
log.Printf("[TRACE] Getting %s of %s plugin from %s", what, opts.PluginRequirement.Identifier, g.Name)
|
||||
// The gitHub plugin we are using because we are not changing the plugin source string, if we decide to change that,
|
||||
// then we need to write this method for release getter as well, but that will change the packer init and install command as well
|
||||
ghURI, err := gh.NewGithubPlugin(opts.PluginRequirement.Identifier)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if g.HttpClient == nil {
|
||||
g.HttpClient = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var req *http.Request
|
||||
transform := transformZipStream()
|
||||
|
||||
switch what {
|
||||
case "releases":
|
||||
// https://releases.hashicorp.com/packer-plugin-docker/index.json
|
||||
url := filepath.ToSlash(officialReleaseURL + ghURI.PluginType() + "/index.json")
|
||||
req, err = http.NewRequest("GET", url, nil)
|
||||
transform = transformReleasesVersionStream
|
||||
case "sha256":
|
||||
// https://releases.hashicorp.com/packer-plugin-docker/8.0.0/packer-plugin-docker_1.1.1_SHA256SUMS
|
||||
url := filepath.ToSlash(officialReleaseURL + ghURI.PluginType() + "/" + opts.VersionString() + "/" + ghURI.PluginType() + "_" + opts.VersionString() + "_SHA256SUMS")
|
||||
transform = gh.TransformChecksumStream()
|
||||
req, err = http.NewRequest("GET", url, nil)
|
||||
case "meta":
|
||||
// https://releases.hashicorp.com/packer-plugin-docker/8.0.0/packer-plugin-docker_1.1.1_manifest.json
|
||||
url := filepath.ToSlash(officialReleaseURL + ghURI.PluginType() + "/" + opts.VersionString() + "/" + ghURI.PluginType() + "_" + opts.VersionString() + "_manifest.json")
|
||||
req, err = http.NewRequest("GET", url, nil)
|
||||
case "zip":
|
||||
// https://releases.hashicorp.com/packer-plugin-docker/1.1.1/packer-plugin-docker_1.1.1_darwin_arm64.zip
|
||||
url := filepath.ToSlash(officialReleaseURL + ghURI.PluginType() + "/" + opts.VersionString() + "/" + opts.ExpectedZipFilename())
|
||||
req, err = http.NewRequest("GET", url, nil)
|
||||
default:
|
||||
return nil, fmt.Errorf("%q not implemented", what)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] http-getter: error creating request for %q: %s", what, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := g.HttpClient.Do(req)
|
||||
if err != nil || resp.StatusCode >= 400 {
|
||||
log.Printf("[ERROR] Got error while trying getting data from releases.hashicorp.com, %v", err)
|
||||
return nil, plugingetter.HTTPFailure
|
||||
}
|
||||
|
||||
defer func(Body io.ReadCloser) {
|
||||
err = Body.Close()
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] http-getter: error closing response body: %s", err)
|
||||
}
|
||||
}(resp.Body)
|
||||
|
||||
return transform(resp.Body)
|
||||
}
|
||||
|
||||
// Init method : a file inside will look like so:
|
||||
//
|
||||
// packer-plugin-comment_0.2.12_freebsd_amd64.zip
|
||||
func (g *Getter) Init(req *plugingetter.Requirement, entry *plugingetter.ChecksumFileEntry) error {
|
||||
filename := entry.Filename
|
||||
//remove the test line below where hardcoded prefix being used
|
||||
res := strings.TrimPrefix(filename, req.FilenamePrefix())
|
||||
// res now looks like v0.2.12_freebsd_amd64.zip
|
||||
|
||||
entry.Ext = filepath.Ext(res)
|
||||
|
||||
res = strings.TrimSuffix(res, entry.Ext)
|
||||
// res now looks like 0.2.12_freebsd_amd64
|
||||
|
||||
parts := strings.Split(res, "_")
|
||||
// ["0.2.12", "freebsd", "amd64"]
|
||||
if len(parts) < 3 {
|
||||
return fmt.Errorf("malformed filename expected %s{version}_{os}_{arch}", req.FilenamePrefix())
|
||||
}
|
||||
|
||||
entry.BinVersion, entry.Os, entry.Arch = parts[0], parts[1], parts[2]
|
||||
entry.BinVersion = strings.TrimPrefix(entry.BinVersion, "v")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Getter) Validate(opt plugingetter.GetOptions, expectedVersion string, installOpts plugingetter.BinaryInstallationOptions, entry *plugingetter.ChecksumFileEntry) error {
|
||||
|
||||
if entry.BinVersion != expectedVersion {
|
||||
return fmt.Errorf("wrong version: %s does not match expected %s", entry.BinVersion, expectedVersion)
|
||||
}
|
||||
if entry.Os != installOpts.OS || entry.Arch != installOpts.ARCH {
|
||||
return fmt.Errorf("wrong system, expected %s_%s got %s_%s", installOpts.OS, installOpts.ARCH, entry.Os, entry.Arch)
|
||||
}
|
||||
|
||||
manifest, err := g.Get("meta", opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var data plugingetter.ManifestMeta
|
||||
body, err := io.ReadAll(manifest)
|
||||
if err != nil {
|
||||
log.Printf("Failed to unmarshal manifest json: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &data)
|
||||
if err != nil {
|
||||
log.Printf("Failed to unmarshal manifest json: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = installOpts.CheckProtocolVersion("x" + data.Metadata.ProtocolVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
g.APIMajor = strings.Split(data.Metadata.ProtocolVersion, ".")[0]
|
||||
g.APIMinor = strings.Split(data.Metadata.ProtocolVersion, ".")[1]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Getter) ExpectedFileName(pr *plugingetter.Requirement, version string, entry *plugingetter.ChecksumFileEntry, zipFileName string) string {
|
||||
pluginSourceParts := strings.Split(pr.Identifier.Source, "/")
|
||||
|
||||
// We need to verify that the plugin source is in the expected format
|
||||
return strings.Join([]string{fmt.Sprintf("packer-plugin-%s", pluginSourceParts[2]),
|
||||
"v" + version,
|
||||
"x" + g.APIMajor + "." + g.APIMinor,
|
||||
entry.Os,
|
||||
entry.Arch + ".zip",
|
||||
}, "_")
|
||||
}
|
||||
@ -0,0 +1,153 @@
|
||||
package release
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/hcl2template/addrs"
|
||||
plugingetter "github.com/hashicorp/packer/packer/plugin-getter"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
entry *plugingetter.ChecksumFileEntry
|
||||
binVersion string
|
||||
protocolVersion string
|
||||
os string
|
||||
arch string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid format parses",
|
||||
entry: &plugingetter.ChecksumFileEntry{
|
||||
Filename: "packer-plugin-v0.2.12_freebsd_amd64.zip",
|
||||
},
|
||||
binVersion: "v0.2.12",
|
||||
protocolVersion: "x5.0",
|
||||
os: "freebsd",
|
||||
arch: "amd64",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "malformed filename returns error",
|
||||
entry: &plugingetter.ChecksumFileEntry{
|
||||
Filename: "packer-plugin-v0.2.12.zip",
|
||||
},
|
||||
binVersion: "v0.2.12",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
req := &plugingetter.Requirement{}
|
||||
|
||||
getter := &Getter{}
|
||||
err := getter.Init(req, tt.entry)
|
||||
|
||||
if err != nil && !tt.wantErr {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
if err == nil && tt.wantErr {
|
||||
t.Fatal("expected error but got nil")
|
||||
}
|
||||
|
||||
if !tt.wantErr && (tt.entry.BinVersion != "0.2.12" || tt.entry.Os != "freebsd" || tt.entry.Arch != "amd64") {
|
||||
t.Fatalf("unexpected parsed values: %+v", tt.entry)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
option plugingetter.GetOptions
|
||||
installOpts plugingetter.BinaryInstallationOptions
|
||||
entry *plugingetter.ChecksumFileEntry
|
||||
version string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "invalid bin version",
|
||||
installOpts: plugingetter.BinaryInstallationOptions{
|
||||
OS: "linux",
|
||||
ARCH: "amd64",
|
||||
},
|
||||
entry: &plugingetter.ChecksumFileEntry{
|
||||
BinVersion: "1.2.3",
|
||||
Os: "linux",
|
||||
Arch: "amd64",
|
||||
},
|
||||
version: "1.2.4",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "wrong OS",
|
||||
installOpts: plugingetter.BinaryInstallationOptions{
|
||||
OS: "linux",
|
||||
ARCH: "amd64",
|
||||
},
|
||||
entry: &plugingetter.ChecksumFileEntry{
|
||||
BinVersion: "1.2.3",
|
||||
Os: "darwin",
|
||||
Arch: "amd64",
|
||||
},
|
||||
version: "1.2.3",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "wrong Arch",
|
||||
installOpts: plugingetter.BinaryInstallationOptions{
|
||||
OS: "linux",
|
||||
ARCH: "amd64",
|
||||
},
|
||||
entry: &plugingetter.ChecksumFileEntry{
|
||||
BinVersion: "1.2.3",
|
||||
Os: "linux",
|
||||
Arch: "arm64",
|
||||
ProtVersion: "x5.0",
|
||||
},
|
||||
version: "1.2.3",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
getter := &Getter{}
|
||||
err := getter.Validate(plugingetter.GetOptions{}, tt.version, tt.installOpts, tt.entry)
|
||||
|
||||
if err != nil && !tt.wantErr {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
if err == nil && tt.wantErr {
|
||||
t.Fatal("expected error but got nil")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpectedFileName(t *testing.T) {
|
||||
getter := &Getter{
|
||||
APIMajor: "5",
|
||||
APIMinor: "0",
|
||||
}
|
||||
pr := plugingetter.Requirement{
|
||||
Identifier: &addrs.Plugin{
|
||||
Source: "github.com/hashicorp/docker",
|
||||
},
|
||||
}
|
||||
|
||||
entry := &plugingetter.ChecksumFileEntry{
|
||||
Os: "linux",
|
||||
Arch: "amd64",
|
||||
}
|
||||
fileName := getter.ExpectedFileName(&pr, "1.2.3", entry, "packer-plugin-docker_v1.2.3_x5.0_linux_amd64.zip")
|
||||
assert.Equal(t, "packer-plugin-docker_v1.2.3_x5.0_linux_amd64.zip", fileName)
|
||||
}
|
||||
Loading…
Reference in new issue