github.com/soltysh/source-to-image@v1.2.0/pkg/scripts/install_test.go (about)

     1  package scripts
     2  
     3  import (
     4  	"fmt"
     5  	"path/filepath"
     6  	"reflect"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/openshift/source-to-image/pkg/api"
    11  	"github.com/openshift/source-to-image/pkg/api/constants"
    12  	dockerpkg "github.com/openshift/source-to-image/pkg/docker"
    13  	"github.com/openshift/source-to-image/pkg/test"
    14  	testfs "github.com/openshift/source-to-image/pkg/test/fs"
    15  	"github.com/openshift/source-to-image/pkg/util/fs"
    16  )
    17  
    18  type fakeScriptManagerConfig struct {
    19  	download Downloader
    20  	docker   dockerpkg.Docker
    21  	fs       fs.FileSystem
    22  	url      string
    23  }
    24  
    25  func newFakeConfig() *fakeScriptManagerConfig {
    26  	return &fakeScriptManagerConfig{
    27  		docker:   &dockerpkg.FakeDocker{},
    28  		download: &test.FakeDownloader{},
    29  		fs:       &testfs.FakeFileSystem{},
    30  		url:      "http://the.scripts.url/s2i/bin",
    31  	}
    32  }
    33  
    34  func newFakeInstaller(config *fakeScriptManagerConfig) Installer {
    35  	m := DefaultScriptSourceManager{
    36  		Image:      "test-image",
    37  		ScriptsURL: config.url,
    38  		docker:     config.docker,
    39  		fs:         config.fs,
    40  		download:   config.download,
    41  	}
    42  	m.Add(&URLScriptHandler{URL: m.ScriptsURL, Download: m.download, FS: m.fs, Name: ScriptURLHandler})
    43  	m.Add(&SourceScriptHandler{fs: m.fs})
    44  	defaultURL, err := m.docker.GetScriptsURL(m.Image)
    45  	if err == nil && defaultURL != "" {
    46  		m.Add(&URLScriptHandler{URL: defaultURL, Download: m.download, FS: m.fs, Name: ImageURLHandler})
    47  	}
    48  	return &m
    49  }
    50  
    51  func isValidInstallResult(result api.InstallResult, t *testing.T) {
    52  	if len(result.Script) == 0 {
    53  		t.Errorf("expected the Script not be empty")
    54  	}
    55  	if result.Error != nil {
    56  		t.Errorf("unexpected the error %v for the %q script in install result", result.Error, result.Script)
    57  	}
    58  	if !result.Downloaded {
    59  		t.Errorf("expected the %q script install result to be downloaded", result.Script)
    60  	}
    61  	if !result.Installed {
    62  		t.Errorf("expected the %q script install result to be installed", result.Script)
    63  	}
    64  	if len(result.URL) == 0 {
    65  		t.Errorf("expected the %q script install result to have valid URL", result.Script)
    66  	}
    67  }
    68  
    69  func TestInstallOptionalFromURL(t *testing.T) {
    70  	config := newFakeConfig()
    71  	inst := newFakeInstaller(config)
    72  	scripts := []string{constants.Assemble, constants.Run}
    73  	results := inst.InstallOptional(scripts, "/output")
    74  	for _, r := range results {
    75  		isValidInstallResult(r, t)
    76  	}
    77  	for _, s := range scripts {
    78  		downloaded := false
    79  		targets := config.download.(*test.FakeDownloader).Target
    80  		for _, t := range targets {
    81  			if filepath.ToSlash(t) == "/output/upload/scripts/"+s {
    82  				downloaded = true
    83  			}
    84  		}
    85  		if !downloaded {
    86  			t.Errorf("the script %q was not downloaded properly (%#v)", s, targets)
    87  		}
    88  		validURL := false
    89  		urls := config.download.(*test.FakeDownloader).URL
    90  		for _, u := range urls {
    91  			if u.String() == config.url+"/"+s {
    92  				validURL = true
    93  			}
    94  		}
    95  		if !validURL {
    96  			t.Errorf("the script %q was downloaded from invalid URL (%+v)", s, urls)
    97  		}
    98  	}
    99  }
   100  
   101  func TestInstallRequiredFromURL(t *testing.T) {
   102  	config := newFakeConfig()
   103  	config.download.(*test.FakeDownloader).Err = map[string]error{
   104  		config.url + "/" + constants.Assemble: fmt.Errorf("download error"),
   105  	}
   106  	inst := newFakeInstaller(config)
   107  	scripts := []string{constants.Assemble, constants.Run}
   108  	_, err := inst.InstallRequired(scripts, "/output")
   109  	if err == nil {
   110  		t.Errorf("expected assemble to fail install")
   111  	}
   112  }
   113  
   114  func TestInstallRequiredFromDocker(t *testing.T) {
   115  	config := newFakeConfig()
   116  	// We fail the download for assemble, which means the Docker image default URL
   117  	// should be used instead.
   118  	config.download.(*test.FakeDownloader).Err = map[string]error{
   119  		config.url + "/" + constants.Assemble: fmt.Errorf("not available"),
   120  	}
   121  	defaultDockerURL := "image:///usr/libexec/s2i/bin"
   122  	config.docker.(*dockerpkg.FakeDocker).DefaultURLResult = defaultDockerURL
   123  	inst := newFakeInstaller(config)
   124  	scripts := []string{constants.Assemble, constants.Run}
   125  	results, err := inst.InstallRequired(scripts, "/output")
   126  	if err != nil {
   127  		t.Errorf("unexpected error, assemble should be installed from docker image url")
   128  	}
   129  	for _, r := range results {
   130  		isValidInstallResult(r, t)
   131  	}
   132  	for _, s := range scripts {
   133  		validURL := false
   134  		urls := config.download.(*test.FakeDownloader).URL
   135  		for _, u := range urls {
   136  			url := config.url
   137  			// The assemble script should be downloaded from image default URL
   138  			if s == constants.Assemble {
   139  				url = defaultDockerURL
   140  			}
   141  			if u.String() == url+"/"+s {
   142  				validURL = true
   143  			}
   144  		}
   145  		if !validURL {
   146  			t.Errorf("the script %q was downloaded from invalid URL (%+v)", s, urls)
   147  		}
   148  	}
   149  }
   150  
   151  func TestInstallRequiredFromSource(t *testing.T) {
   152  	config := newFakeConfig()
   153  	// There is no other script source than the source code
   154  	config.url = ""
   155  	deprecatedSourceScripts := strings.Replace(constants.SourceScripts, ".s2i", ".sti", -1)
   156  	config.fs.(*testfs.FakeFileSystem).ExistsResult = map[string]bool{
   157  		filepath.Join("/workdir", constants.SourceScripts, constants.Assemble): true,
   158  		filepath.Join("/workdir", deprecatedSourceScripts, constants.Run):      true,
   159  	}
   160  	inst := newFakeInstaller(config)
   161  	scripts := []string{constants.Assemble, constants.Run}
   162  	result, err := inst.InstallRequired(scripts, "/workdir")
   163  	if err != nil {
   164  		t.Errorf("unexpected error, assemble should be installed from docker image url: %v", err)
   165  	}
   166  	for _, r := range result {
   167  		isValidInstallResult(r, t)
   168  	}
   169  	for _, s := range scripts {
   170  		validResultURL := false
   171  		for _, r := range result {
   172  			// The constants.Run use deprecated path, but it should still work.
   173  			if s == constants.Run && r.URL == filepath.FromSlash(sourcesRootAbbrev+"/.sti/bin/"+s) {
   174  				validResultURL = true
   175  			}
   176  			if r.URL == filepath.FromSlash(sourcesRootAbbrev+"/.s2i/bin/"+s) {
   177  				validResultURL = true
   178  			}
   179  		}
   180  		if !validResultURL {
   181  			t.Errorf("expected %q has result URL %s, got %#v", s, filepath.FromSlash(sourcesRootAbbrev+"/.s2i/bin/"+s), result)
   182  		}
   183  		chmodCalled := false
   184  		filesystem := config.fs.(*testfs.FakeFileSystem)
   185  		for _, f := range filesystem.ChmodFile {
   186  			if filepath.ToSlash(f) == "/workdir/upload/scripts/"+s {
   187  				chmodCalled = true
   188  			}
   189  		}
   190  		if !chmodCalled {
   191  			t.Errorf("expected chmod called on /workdir/upload/scripts/%s", s)
   192  		}
   193  	}
   194  }
   195  
   196  // TestInstallRequiredOrder tests the proper order for retrieving the source
   197  // scripts.
   198  // The scenario here is that the assemble script does not exists in provided
   199  // scripts url, but it exists in source code directory. The save-artifacts does
   200  // not exists at provided url nor in source code, so the docker image default
   201  // URL should be used.
   202  func TestInstallRequiredOrder(t *testing.T) {
   203  	config := newFakeConfig()
   204  	config.download.(*test.FakeDownloader).Err = map[string]error{
   205  		config.url + "/" + constants.Assemble:      fmt.Errorf("not available"),
   206  		config.url + "/" + constants.SaveArtifacts: fmt.Errorf("not available"),
   207  	}
   208  	config.fs.(*testfs.FakeFileSystem).ExistsResult = map[string]bool{
   209  		filepath.Join("/workdir", constants.SourceScripts, constants.Assemble):      true,
   210  		filepath.Join("/workdir", constants.SourceScripts, constants.Run):           false,
   211  		filepath.Join("/workdir", constants.SourceScripts, constants.SaveArtifacts): false,
   212  	}
   213  	defaultDockerURL := "http://the.docker.url/s2i"
   214  	config.docker.(*dockerpkg.FakeDocker).DefaultURLResult = defaultDockerURL
   215  	scripts := []string{constants.Assemble, constants.Run, constants.SaveArtifacts}
   216  	inst := newFakeInstaller(config)
   217  	result, err := inst.InstallRequired(scripts, "/workdir")
   218  	if err != nil {
   219  		t.Errorf("unexpected error: %v", err)
   220  	}
   221  	for _, r := range result {
   222  		isValidInstallResult(r, t)
   223  	}
   224  	for _, s := range scripts {
   225  		found := false
   226  		for _, r := range result {
   227  			if r.Script == s && r.Script == constants.Assemble && r.URL == filepath.FromSlash(sourcesRootAbbrev+"/.s2i/bin/assemble") {
   228  				found = true
   229  				break
   230  			}
   231  			if r.Script == s && r.Script == constants.Run && r.URL == config.url+"/"+constants.Run {
   232  				found = true
   233  				break
   234  			}
   235  			if r.Script == s && r.Script == constants.SaveArtifacts && r.URL == defaultDockerURL+"/"+constants.SaveArtifacts {
   236  				found = true
   237  				break
   238  			}
   239  		}
   240  		if !found {
   241  			t.Errorf("the %q script installed in wrong order: %+v", s, result)
   242  		}
   243  	}
   244  }
   245  
   246  func TestInstallRequiredError(t *testing.T) {
   247  	config := newFakeConfig()
   248  	config.url = ""
   249  	scripts := []string{constants.Assemble, constants.Run}
   250  	inst := newFakeInstaller(config)
   251  	result, err := inst.InstallRequired(scripts, "/output")
   252  	if err == nil {
   253  		t.Errorf("expected error, got %+v", result)
   254  	}
   255  }
   256  
   257  func TestInstallRequiredFromInvalidURL(t *testing.T) {
   258  	config := newFakeConfig()
   259  	config.url = "../invalid-url"
   260  	scripts := []string{constants.Assemble}
   261  	inst := newFakeInstaller(config)
   262  	result, err := inst.InstallRequired(scripts, "/output")
   263  	if err == nil {
   264  		t.Errorf("expected error, got %+v", result)
   265  	}
   266  }
   267  
   268  func TestNewInstaller(t *testing.T) {
   269  	docker := &dockerpkg.FakeDocker{DefaultURLResult: "image://docker"}
   270  	inst := NewInstaller("test-image", "http://foo.bar", nil, docker, api.AuthConfig{}, &testfs.FakeFileSystem{})
   271  	sources := inst.(*DefaultScriptSourceManager).sources
   272  	firstHandler, ok := sources[0].(*URLScriptHandler)
   273  	if !ok {
   274  		t.Errorf("expected first handler to be script url handler, got %#v", inst.(*DefaultScriptSourceManager).sources)
   275  	}
   276  	if firstHandler.URL != "http://foo.bar" {
   277  		t.Errorf("expected first handler to handle the script url, got %+v", firstHandler)
   278  	}
   279  	lastHandler, ok := sources[len(sources)-1].(*URLScriptHandler)
   280  	if !ok {
   281  		t.Errorf("expected last handler to be docker url handler, got %#v", inst.(*DefaultScriptSourceManager).sources)
   282  	}
   283  	if lastHandler.URL != "image://docker" {
   284  		t.Errorf("expected last handler to handle the docker default url, got %+v", lastHandler)
   285  	}
   286  }
   287  
   288  type fakeSource struct {
   289  	name   string
   290  	failOn map[string]struct{}
   291  }
   292  
   293  func (f *fakeSource) Get(script string) *api.InstallResult {
   294  	return &api.InstallResult{Script: script}
   295  }
   296  
   297  func (f *fakeSource) Install(r *api.InstallResult) error {
   298  	if _, fail := f.failOn[r.Script]; fail {
   299  		return fmt.Errorf("error")
   300  	}
   301  	return nil
   302  }
   303  
   304  func (f *fakeSource) SetDestinationDir(string) {}
   305  
   306  func (f *fakeSource) String() string {
   307  	return f.name
   308  }
   309  
   310  func TestInstallOptionalFailedSources(t *testing.T) {
   311  
   312  	m := DefaultScriptSourceManager{}
   313  	m.Add(&fakeSource{name: "failing1", failOn: map[string]struct{}{"one": {}, "two": {}, "three": {}}})
   314  	m.Add(&fakeSource{name: "failing2", failOn: map[string]struct{}{"one": {}, "two": {}, "three": {}}})
   315  	m.Add(&fakeSource{name: "almostpassing", failOn: map[string]struct{}{"three": {}}})
   316  
   317  	expect := map[string][]string{
   318  		"one":   {"failing1", "failing2"},
   319  		"two":   {"failing1", "failing2"},
   320  		"three": {"failing1", "failing2", "almostpassing"},
   321  	}
   322  	results := m.InstallOptional([]string{"one", "two", "three"}, "foo")
   323  	for _, result := range results {
   324  		if !reflect.DeepEqual(result.FailedSources, expect[result.Script]) {
   325  			t.Errorf("Did not get expected failed sources: %#v", result)
   326  		}
   327  	}
   328  }