github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/testcharms/charm.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package testcharms 5 6 import ( 7 "archive/zip" 8 "bytes" 9 "io" 10 "os" 11 "time" 12 13 "github.com/juju/charm/v12" 14 jc "github.com/juju/testing/checkers" 15 gc "gopkg.in/check.v1" 16 17 "github.com/juju/juju/testcharms/repo" 18 jtesting "github.com/juju/juju/testing" 19 ) 20 21 const ( 22 defaultSeries = "quantal" 23 localCharmRepo = "charm-repo" 24 localCharmHub = "charm-hub" 25 ) 26 27 // Repo provides access to the test charm repository. 28 var Repo = repo.NewRepo(localCharmRepo, defaultSeries) 29 30 // Hub provides access to the test charmhub repository. 31 var Hub = repo.NewRepo(localCharmHub, defaultSeries) 32 33 // RepoForSeries returns a new charm repository for the specified series. 34 // Note: this is a bit weird, as it ignores the series if it's NOT kubernetes 35 // and falls back to the default series, which makes this pretty pointless. 36 func RepoForSeries(series string) *repo.CharmRepo { 37 return repo.NewRepo(localCharmRepo, series) 38 } 39 40 // RepoWithSeries returns a new charm repository for the specified series. 41 func RepoWithSeries(series string) *repo.CharmRepo { 42 return repo.NewRepo(localCharmRepo, series) 43 } 44 45 // CharmRepo returns a new charm repository. 46 func CharmRepo() *repo.CharmRepo { 47 return repo.NewRepo("charms", "") 48 } 49 50 // CheckCharmReady ensures that a desired charm archive exists and 51 // has some content. 52 func CheckCharmReady(c *gc.C, charmArchive *charm.CharmArchive) { 53 fileSize := func() int64 { 54 f, err := os.Open(charmArchive.Path) 55 c.Assert(err, jc.ErrorIsNil) 56 defer func() { _ = f.Close() }() 57 58 fi, err := f.Stat() 59 c.Assert(err, jc.ErrorIsNil) 60 return fi.Size() 61 } 62 63 var oldSize, currentSize int64 64 var charmReady bool 65 runs := 1 66 timeout := time.After(jtesting.LongWait) 67 for !charmReady { 68 select { 69 case <-time.After(jtesting.ShortWait): 70 currentSize = fileSize() 71 // Since we do not know when the charm is ready, for as long as the size changes 72 // we'll assume that we'd need to wait. 73 charmReady = oldSize != 0 && currentSize == oldSize 74 c.Logf("%d: new file size %v (old size %v)", runs, currentSize, oldSize) 75 oldSize = currentSize 76 runs++ 77 case <-timeout: 78 c.Fatalf("timed out waiting for charm @%v to be ready", charmArchive.Path) 79 } 80 } 81 } 82 83 // InjectFilesToCharmArchive overwrites the contents of pathToArchive with a 84 // new archive containing the original files plus the ones provided in the 85 // fileContents map (key: file name, value: file contents). 86 func InjectFilesToCharmArchive(pathToArchive string, fileContents map[string]string) error { 87 zr, err := zip.OpenReader(pathToArchive) 88 if err != nil { 89 return err 90 } 91 defer func() { _ = zr.Close() }() 92 93 var buf bytes.Buffer 94 zw := zip.NewWriter(&buf) 95 defer func() { _ = zw.Close() }() 96 97 // Copy existing files 98 for _, f := range zr.File { 99 w, err := zw.CreateHeader(&f.FileHeader) 100 if err != nil { 101 return err 102 } 103 104 r, err := f.Open() 105 if err != nil { 106 return err 107 } 108 109 _, err = io.Copy(w, r) 110 _ = r.Close() 111 if err != nil { 112 return err 113 } 114 } 115 116 // Add new files 117 for name, contents := range fileContents { 118 w, err := zw.Create(name) 119 if err != nil { 120 return err 121 } 122 123 if _, err = w.Write([]byte(contents)); err != nil { 124 return err 125 } 126 } 127 128 // Overwrite original archive with the patched version 129 _, _ = zr.Close(), zw.Close() 130 return os.WriteFile(pathToArchive, buf.Bytes(), 0644) 131 }