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  }