get.porter.sh/porter@v1.3.0/pkg/pkgmgmt/client/install_test.go (about)

     1  package client
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"os"
    10  	"path"
    11  	"runtime"
    12  	"strings"
    13  	"testing"
    14  
    15  	"get.porter.sh/porter/pkg"
    16  	"get.porter.sh/porter/pkg/config"
    17  	"get.porter.sh/porter/pkg/pkgmgmt"
    18  	"get.porter.sh/porter/tests"
    19  	"github.com/stretchr/testify/assert"
    20  	"github.com/stretchr/testify/require"
    21  )
    22  
    23  func TestFileSystem_InstallFromUrl(t *testing.T) {
    24  	testcases := []struct {
    25  		name         string
    26  		os           string
    27  		arch         string
    28  		responseCode map[string]int
    29  		wantError    string
    30  	}{
    31  		{name: "darwin/arm64 fallback to amd64", os: "darwin", arch: "arm64", responseCode: map[string]int{"arm64": 404}},
    32  		{name: "darwin/arm64 binary exists", os: "darwin", arch: "arm64"},
    33  		{name: "non-darwin arm64 no special handling", os: "myos", arch: "arm64", responseCode: map[string]int{"arm64": 404}, wantError: "404 Not Found"},
    34  	}
    35  
    36  	for _, tc := range testcases {
    37  		t.Run(tc.name, func(t *testing.T) {
    38  			// serve out a fake package
    39  			ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    40  				for term, code := range tc.responseCode {
    41  					if strings.Contains(r.RequestURI, term) {
    42  						w.WriteHeader(code)
    43  						break
    44  					}
    45  				}
    46  				fmt.Fprintf(w, "#!/usr/bin/env bash\necho i am a random package\n")
    47  			}))
    48  			defer ts.Close()
    49  
    50  			c := config.NewTestConfig(t)
    51  			p := NewFileSystem(c.Config, "packages")
    52  
    53  			opts := pkgmgmt.InstallOptions{
    54  				PackageType: "mixin",
    55  				Version:     "latest",
    56  				URL:         ts.URL,
    57  			}
    58  			err := opts.Validate([]string{"mypkg"})
    59  			require.NoError(t, err, "Validate failed")
    60  
    61  			err = p.installFromURLFor(context.Background(), opts, tc.os, tc.arch)
    62  			if tc.wantError != "" {
    63  				tests.RequireErrorContains(t, err, tc.wantError)
    64  			} else {
    65  				require.NoError(t, err)
    66  				clientPath := "/home/myuser/.porter/packages/mypkg/mypkg"
    67  				if runtime.GOOS == "windows" {
    68  					clientPath += ".exe"
    69  				}
    70  				clientStats, err := p.FileSystem.Stat(clientPath)
    71  				require.NoError(t, err)
    72  				wantMode := pkg.FileModeExecutable
    73  				tests.AssertFilePermissionsEqual(t, clientPath, wantMode, clientStats.Mode())
    74  
    75  				runtimePath := "/home/myuser/.porter/packages/mypkg/runtimes/mypkg-runtime"
    76  				runtimeStats, _ := p.FileSystem.Stat(runtimePath)
    77  				require.NoError(t, err)
    78  				tests.AssertFilePermissionsEqual(t, runtimePath, wantMode, runtimeStats.Mode())
    79  			}
    80  		})
    81  	}
    82  }
    83  
    84  func TestFileSystem_InstallFromFeedUrl(t *testing.T) {
    85  	if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
    86  		t.Skip("skipping because there is no release for helm for darwin/arm64")
    87  	}
    88  
    89  	var testURL = ""
    90  	feed, err := os.ReadFile("../feed/testdata/atom.xml")
    91  	require.NoError(t, err)
    92  
    93  	// serve out a fake feed and package
    94  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    95  		if strings.HasSuffix(r.RequestURI, "atom.xml") {
    96  			// swap out the urls in the test atom feed to match the test http server here so that porter downloads
    97  			// the package binaries from the fake server
    98  			testAtom := strings.Replace(string(feed), "https://cdn.porter.sh", testURL, -1)
    99  			fmt.Fprintln(w, testAtom)
   100  		} else {
   101  			fmt.Fprintf(w, "#!/usr/bin/env bash\necho i am helm\n")
   102  		}
   103  	}))
   104  	defer ts.Close()
   105  	testURL = ts.URL
   106  
   107  	c := config.NewTestConfig(t)
   108  	p := NewFileSystem(c.Config, "packages")
   109  
   110  	opts := pkgmgmt.InstallOptions{
   111  		PackageType: "plugin",
   112  		Version:     "v1.2.4",
   113  		FeedURL:     ts.URL + "/atom.xml",
   114  	}
   115  	err = opts.Validate([]string{"helm"})
   116  	require.NoError(t, err, "Validate failed")
   117  
   118  	err = p.Install(context.Background(), opts)
   119  	require.NoError(t, err)
   120  
   121  	clientExists, _ := p.FileSystem.Exists("/home/myuser/.porter/packages/helm/helm")
   122  	if runtime.GOOS == "windows" {
   123  		clientExists, _ = p.FileSystem.Exists("/home/myuser/.porter/packages/helm/helm.exe")
   124  	}
   125  	assert.True(t, clientExists)
   126  	runtimeExists, _ := p.FileSystem.Exists("/home/myuser/.porter/packages/helm/runtimes/helm-runtime")
   127  	assert.True(t, runtimeExists)
   128  }
   129  
   130  func TestFileSystem_Install_RollbackMissingRuntime(t *testing.T) {
   131  	// serve out a fake package
   132  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   133  		if strings.Contains(r.RequestURI, "linux-amd64") {
   134  			w.WriteHeader(400)
   135  		} else {
   136  			fmt.Fprintf(w, "#!/usr/bin/env bash\necho i am a client mypkg\n")
   137  		}
   138  	}))
   139  	defer ts.Close()
   140  
   141  	c := config.NewTestConfig(t)
   142  	p := NewFileSystem(c.Config, "packages")
   143  
   144  	parentDir, _ := p.GetPackagesDir()
   145  	pkgDir := path.Join(parentDir, "mypkg")
   146  
   147  	opts := pkgmgmt.InstallOptions{
   148  		PackageType: "mixin",
   149  		Version:     "latest",
   150  		URL:         ts.URL,
   151  	}
   152  	err := opts.Validate([]string{"mypkg"})
   153  	require.NoError(t, err, "Validate failed")
   154  
   155  	err = p.Install(context.Background(), opts)
   156  	require.Error(t, err)
   157  	assert.Contains(t, err.Error(), "bad status returned when downloading")
   158  
   159  	// Make sure the package directory was removed
   160  	dirExists, _ := p.FileSystem.DirExists(pkgDir)
   161  	assert.False(t, dirExists)
   162  }
   163  
   164  func TestFileSystem_Install_PackageInfoSavedWhenNoFileExists(t *testing.T) {
   165  	c := config.NewTestConfig(t)
   166  	p := NewFileSystem(c.Config, "packages")
   167  
   168  	packageURL := "https://cdn.porter.sh/mixins/helm"
   169  	opts := pkgmgmt.InstallOptions{
   170  		PackageType: "plugin",
   171  		Version:     "v1.2.4",
   172  		URL:         packageURL,
   173  	}
   174  	name := "helm"
   175  	err := opts.Validate([]string{name})
   176  	require.NoError(t, err, "Validate failed")
   177  
   178  	// ensure cache.json does not exist (yet)
   179  	cacheExists, _ := p.FileSystem.Exists("/home/myuser/.porter/packages/cache.json")
   180  	assert.False(t, cacheExists)
   181  
   182  	err = p.savePackageInfo(context.Background(), opts)
   183  	require.NoError(t, err)
   184  
   185  	// cache.json should have been created
   186  	cacheExists, _ = p.FileSystem.Exists("/home/myuser/.porter/packages/cache.json")
   187  	assert.True(t, cacheExists)
   188  
   189  	cacheContentsB, err := p.FileSystem.ReadFile("/home/myuser/.porter/packages/cache.json")
   190  	require.NoError(t, err)
   191  
   192  	//read cache.json
   193  	var allPackages packages
   194  	err = json.Unmarshal(cacheContentsB, &allPackages)
   195  	require.NoError(t, err)
   196  
   197  	//confirm that the required pkg is present
   198  	var pkgData PackageInfo
   199  	for _, pkg := range allPackages.Packages {
   200  		if pkg.Name == name {
   201  			pkgData = pkg
   202  			break
   203  		}
   204  	}
   205  
   206  	assert.Equal(t, name, pkgData.Name)
   207  	assert.Equal(t, packageURL, pkgData.URL)
   208  }