github.imxd.top/openshift/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 }