github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/environs/tools/testing/testing.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package testing
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"os"
    11  	"path"
    12  	"path/filepath"
    13  	"sort"
    14  	"strings"
    15  	"time"
    16  
    17  	gc "launchpad.net/gocheck"
    18  
    19  	"launchpad.net/juju-core/environs/filestorage"
    20  	"launchpad.net/juju-core/environs/simplestreams"
    21  	"launchpad.net/juju-core/environs/storage"
    22  	"launchpad.net/juju-core/environs/tools"
    23  	jc "launchpad.net/juju-core/testing/checkers"
    24  	coretools "launchpad.net/juju-core/tools"
    25  	"launchpad.net/juju-core/utils"
    26  	"launchpad.net/juju-core/utils/set"
    27  	"launchpad.net/juju-core/version"
    28  )
    29  
    30  // MakeTools creates some fake tools with the given version strings.
    31  func MakeTools(c *gc.C, metadataDir, subdir string, versionStrings []string) {
    32  	makeTools(c, metadataDir, subdir, versionStrings, false)
    33  }
    34  
    35  // MakeTools creates some fake tools (including checksums) with the given version strings.
    36  func MakeToolsWithCheckSum(c *gc.C, metadataDir, subdir string, versionStrings []string) {
    37  	makeTools(c, metadataDir, subdir, versionStrings, true)
    38  }
    39  
    40  func makeTools(c *gc.C, metadataDir, subdir string, versionStrings []string, withCheckSum bool) {
    41  	toolsDir := filepath.Join(metadataDir, storage.BaseToolsPath)
    42  	if subdir != "" {
    43  		toolsDir = filepath.Join(toolsDir, subdir)
    44  	}
    45  	c.Assert(os.MkdirAll(toolsDir, 0755), gc.IsNil)
    46  	var toolsList coretools.List
    47  	for _, versionString := range versionStrings {
    48  		binary := version.MustParseBinary(versionString)
    49  		path := filepath.Join(toolsDir, fmt.Sprintf("juju-%s.tgz", binary))
    50  		data := binary.String()
    51  		err := ioutil.WriteFile(path, []byte(data), 0644)
    52  		c.Assert(err, gc.IsNil)
    53  		tool := &coretools.Tools{
    54  			Version: binary,
    55  			URL:     path,
    56  		}
    57  		if withCheckSum {
    58  			tool.Size, tool.SHA256 = SHA256sum(c, path)
    59  		}
    60  		toolsList = append(toolsList, tool)
    61  	}
    62  	// Write the tools metadata.
    63  	stor, err := filestorage.NewFileStorageWriter(metadataDir)
    64  	c.Assert(err, gc.IsNil)
    65  	err = tools.MergeAndWriteMetadata(stor, toolsList, false)
    66  	c.Assert(err, gc.IsNil)
    67  }
    68  
    69  // SHA256sum creates the sha256 checksum for the specified file.
    70  func SHA256sum(c *gc.C, path string) (int64, string) {
    71  	if strings.HasPrefix(path, "file://") {
    72  		path = path[len("file://"):]
    73  	}
    74  	hash, size, err := utils.ReadFileSHA256(path)
    75  	c.Assert(err, gc.IsNil)
    76  	return size, hash
    77  }
    78  
    79  // ParseMetadataFromDir loads ToolsMetadata from the specified directory.
    80  func ParseMetadataFromDir(c *gc.C, metadataDir string, expectMirrors bool) []*tools.ToolsMetadata {
    81  	stor, err := filestorage.NewFileStorageReader(metadataDir)
    82  	c.Assert(err, gc.IsNil)
    83  	return ParseMetadataFromStorage(c, stor, expectMirrors)
    84  }
    85  
    86  // ParseMetadataFromStorage loads ToolsMetadata from the specified storage reader.
    87  func ParseMetadataFromStorage(c *gc.C, stor storage.StorageReader, expectMirrors bool) []*tools.ToolsMetadata {
    88  	source := storage.NewStorageSimpleStreamsDataSource("test storage reader", stor, "tools")
    89  	params := simplestreams.ValueParams{
    90  		DataType:      tools.ContentDownload,
    91  		ValueTemplate: tools.ToolsMetadata{},
    92  	}
    93  
    94  	const requireSigned = false
    95  	indexPath := simplestreams.UnsignedIndex
    96  	indexRef, err := simplestreams.GetIndexWithFormat(
    97  		source, indexPath, "index:1.0", requireSigned, simplestreams.CloudSpec{}, params)
    98  	c.Assert(err, gc.IsNil)
    99  	c.Assert(indexRef.Indexes, gc.HasLen, 1)
   100  
   101  	toolsIndexMetadata := indexRef.Indexes["com.ubuntu.juju:released:tools"]
   102  	c.Assert(toolsIndexMetadata, gc.NotNil)
   103  
   104  	// Read the products file contents.
   105  	r, err := stor.Get(path.Join("tools", toolsIndexMetadata.ProductsFilePath))
   106  	defer r.Close()
   107  	c.Assert(err, gc.IsNil)
   108  	data, err := ioutil.ReadAll(r)
   109  	c.Assert(err, gc.IsNil)
   110  
   111  	url, err := source.URL(toolsIndexMetadata.ProductsFilePath)
   112  	c.Assert(err, gc.IsNil)
   113  	cloudMetadata, err := simplestreams.ParseCloudMetadata(data, "products:1.0", url, tools.ToolsMetadata{})
   114  	c.Assert(err, gc.IsNil)
   115  
   116  	toolsMetadataMap := make(map[string]*tools.ToolsMetadata)
   117  	var expectedProductIds set.Strings
   118  	var toolsVersions set.Strings
   119  	for _, mc := range cloudMetadata.Products {
   120  		for _, items := range mc.Items {
   121  			for key, item := range items.Items {
   122  				toolsMetadata := item.(*tools.ToolsMetadata)
   123  				toolsMetadataMap[key] = toolsMetadata
   124  				toolsVersions.Add(key)
   125  				seriesVersion, err := simplestreams.SeriesVersion(toolsMetadata.Release)
   126  				c.Assert(err, gc.IsNil)
   127  				productId := fmt.Sprintf("com.ubuntu.juju:%s:%s", seriesVersion, toolsMetadata.Arch)
   128  				expectedProductIds.Add(productId)
   129  			}
   130  		}
   131  	}
   132  
   133  	// Make sure index's product IDs are all represented in the products metadata.
   134  	sort.Strings(toolsIndexMetadata.ProductIds)
   135  	c.Assert(toolsIndexMetadata.ProductIds, gc.DeepEquals, expectedProductIds.SortedValues())
   136  
   137  	toolsMetadata := make([]*tools.ToolsMetadata, len(toolsMetadataMap))
   138  	for i, key := range toolsVersions.SortedValues() {
   139  		toolsMetadata[i] = toolsMetadataMap[key]
   140  	}
   141  
   142  	if expectMirrors {
   143  		r, err = stor.Get(path.Join("tools", simplestreams.UnsignedMirror))
   144  		defer r.Close()
   145  		c.Assert(err, gc.IsNil)
   146  		data, err = ioutil.ReadAll(r)
   147  		c.Assert(err, gc.IsNil)
   148  		c.Assert(string(data), jc.Contains, `"mirrors":`)
   149  		c.Assert(err, gc.IsNil)
   150  	}
   151  	return toolsMetadata
   152  }
   153  
   154  type metadataFile struct {
   155  	path string
   156  	data []byte
   157  }
   158  
   159  func generateMetadata(c *gc.C, versions ...version.Binary) []metadataFile {
   160  	var metadata = make([]*tools.ToolsMetadata, len(versions))
   161  	for i, vers := range versions {
   162  		basePath := fmt.Sprintf("releases/tools-%s.tar.gz", vers.String())
   163  		metadata[i] = &tools.ToolsMetadata{
   164  			Release: vers.Series,
   165  			Version: vers.Number.String(),
   166  			Arch:    vers.Arch,
   167  			Path:    basePath,
   168  		}
   169  	}
   170  	index, products, err := tools.MarshalToolsMetadataJSON(metadata, time.Now())
   171  	c.Assert(err, gc.IsNil)
   172  	objects := []metadataFile{
   173  		{simplestreams.UnsignedIndex, index},
   174  		{tools.ProductMetadataPath, products},
   175  	}
   176  	return objects
   177  }
   178  
   179  // UploadToStorage uploads tools and metadata for the specified versions to storage.
   180  func UploadToStorage(c *gc.C, stor storage.Storage, versions ...version.Binary) map[version.Binary]string {
   181  	uploaded := map[version.Binary]string{}
   182  	if len(versions) == 0 {
   183  		return uploaded
   184  	}
   185  	var err error
   186  	for _, vers := range versions {
   187  		filename := fmt.Sprintf("tools/releases/tools-%s.tar.gz", vers.String())
   188  		// Put a file in images since the dummy storage provider requires a
   189  		// file to exist before the URL can be found. This is to ensure it behaves
   190  		// the same way as MAAS.
   191  		err = stor.Put(filename, strings.NewReader("dummy"), 5)
   192  		c.Assert(err, gc.IsNil)
   193  		uploaded[vers], err = stor.URL(filename)
   194  		c.Assert(err, gc.IsNil)
   195  	}
   196  	objects := generateMetadata(c, versions...)
   197  	for _, object := range objects {
   198  		toolspath := path.Join("tools", object.path)
   199  		err = stor.Put(toolspath, bytes.NewReader(object.data), int64(len(object.data)))
   200  		c.Assert(err, gc.IsNil)
   201  	}
   202  	return uploaded
   203  }
   204  
   205  // UploadToStorage uploads tools and metadata for the specified versions to dir.
   206  func UploadToDirectory(c *gc.C, dir string, versions ...version.Binary) map[version.Binary]string {
   207  	uploaded := map[version.Binary]string{}
   208  	if len(versions) == 0 {
   209  		return uploaded
   210  	}
   211  	for _, vers := range versions {
   212  		basePath := fmt.Sprintf("releases/tools-%s.tar.gz", vers.String())
   213  		uploaded[vers] = fmt.Sprintf("file://%s/%s", dir, basePath)
   214  	}
   215  	objects := generateMetadata(c, versions...)
   216  	for _, object := range objects {
   217  		path := filepath.Join(dir, object.path)
   218  		dir := filepath.Dir(path)
   219  		if err := os.MkdirAll(dir, 0755); err != nil && !os.IsExist(err) {
   220  			c.Assert(err, gc.IsNil)
   221  		}
   222  		err := ioutil.WriteFile(path, object.data, 0644)
   223  		c.Assert(err, gc.IsNil)
   224  	}
   225  	return uploaded
   226  }