github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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  	"crypto/sha256"
     9  	"fmt"
    10  	"io"
    11  	"io/ioutil"
    12  	"os"
    13  	"path"
    14  	"path/filepath"
    15  	"sort"
    16  	"strings"
    17  	"time"
    18  
    19  	jc "github.com/juju/testing/checkers"
    20  	"github.com/juju/utils"
    21  	"github.com/juju/utils/series"
    22  	"github.com/juju/utils/set"
    23  	gc "gopkg.in/check.v1"
    24  
    25  	"github.com/juju/juju/environs/filestorage"
    26  	"github.com/juju/juju/environs/simplestreams"
    27  	"github.com/juju/juju/environs/storage"
    28  	"github.com/juju/juju/environs/sync"
    29  	"github.com/juju/juju/environs/tools"
    30  	"github.com/juju/juju/juju/names"
    31  	coretesting "github.com/juju/juju/testing"
    32  	coretools "github.com/juju/juju/tools"
    33  	"github.com/juju/juju/version"
    34  )
    35  
    36  func GetMockBundleTools(c *gc.C) tools.BundleToolsFunc {
    37  	return func(w io.Writer, forceVersion *version.Number) (vers version.Binary, sha256Hash string, err error) {
    38  		vers = version.Current
    39  		if forceVersion != nil {
    40  			vers.Number = *forceVersion
    41  		}
    42  		sha256Hash = fmt.Sprintf("%x", sha256.New().Sum(nil))
    43  		return vers, sha256Hash, err
    44  	}
    45  }
    46  
    47  // GetMockBuildTools returns a sync.BuildToolsTarballFunc implementation which generates
    48  // a fake tools tarball.
    49  func GetMockBuildTools(c *gc.C) sync.BuildToolsTarballFunc {
    50  	return func(forceVersion *version.Number, stream string) (*sync.BuiltTools, error) {
    51  		vers := version.Current
    52  		if forceVersion != nil {
    53  			vers.Number = *forceVersion
    54  		}
    55  
    56  		tgz, checksum := coretesting.TarGz(
    57  			coretesting.NewTarFile(names.Jujud, 0777, "jujud contents "+vers.String()))
    58  
    59  		toolsDir, err := ioutil.TempDir("", "juju-tools-"+stream)
    60  		c.Assert(err, jc.ErrorIsNil)
    61  		name := "name"
    62  		ioutil.WriteFile(filepath.Join(toolsDir, name), tgz, 0777)
    63  
    64  		return &sync.BuiltTools{
    65  			Dir:         toolsDir,
    66  			StorageName: name,
    67  			Version:     vers,
    68  			Size:        int64(len(tgz)),
    69  			Sha256Hash:  checksum,
    70  		}, nil
    71  	}
    72  }
    73  
    74  // MakeTools creates some fake tools with the given version strings.
    75  func MakeTools(c *gc.C, metadataDir, stream string, versionStrings []string) coretools.List {
    76  	return makeTools(c, metadataDir, stream, versionStrings, false)
    77  }
    78  
    79  // MakeToolsWithCheckSum creates some fake tools (including checksums) with the given version strings.
    80  func MakeToolsWithCheckSum(c *gc.C, metadataDir, stream string, versionStrings []string) coretools.List {
    81  	return makeTools(c, metadataDir, stream, versionStrings, true)
    82  }
    83  
    84  func makeTools(c *gc.C, metadataDir, stream string, versionStrings []string, withCheckSum bool) coretools.List {
    85  	toolsDir := filepath.Join(metadataDir, storage.BaseToolsPath, stream)
    86  	c.Assert(os.MkdirAll(toolsDir, 0755), gc.IsNil)
    87  	var toolsList coretools.List
    88  	for _, versionString := range versionStrings {
    89  		binary, err := version.ParseBinary(versionString)
    90  		if err != nil {
    91  			c.Assert(err, jc.Satisfies, series.IsUnknownOSForSeriesError)
    92  		}
    93  		path := filepath.Join(toolsDir, fmt.Sprintf("juju-%s.tgz", binary))
    94  		data := binary.String()
    95  		err = ioutil.WriteFile(path, []byte(data), 0644)
    96  		c.Assert(err, jc.ErrorIsNil)
    97  		tool := &coretools.Tools{
    98  			Version: binary,
    99  			URL:     path,
   100  		}
   101  		if withCheckSum {
   102  			tool.Size, tool.SHA256 = SHA256sum(c, path)
   103  		}
   104  		toolsList = append(toolsList, tool)
   105  	}
   106  	// Write the tools metadata.
   107  	stor, err := filestorage.NewFileStorageWriter(metadataDir)
   108  	c.Assert(err, jc.ErrorIsNil)
   109  	err = tools.MergeAndWriteMetadata(stor, stream, stream, toolsList, false)
   110  	c.Assert(err, jc.ErrorIsNil)
   111  	return toolsList
   112  }
   113  
   114  // SHA256sum creates the sha256 checksum for the specified file.
   115  func SHA256sum(c *gc.C, path string) (int64, string) {
   116  	if strings.HasPrefix(path, "file://") {
   117  		path = path[len("file://"):]
   118  	}
   119  	hash, size, err := utils.ReadFileSHA256(path)
   120  	c.Assert(err, jc.ErrorIsNil)
   121  	return size, hash
   122  }
   123  
   124  // ParseMetadataFromDir loads ToolsMetadata from the specified directory.
   125  func ParseMetadataFromDir(c *gc.C, metadataDir, stream string, expectMirrors bool) []*tools.ToolsMetadata {
   126  	stor, err := filestorage.NewFileStorageReader(metadataDir)
   127  	c.Assert(err, jc.ErrorIsNil)
   128  	return ParseMetadataFromStorage(c, stor, stream, expectMirrors)
   129  }
   130  
   131  // ParseMetadataFromStorage loads ToolsMetadata from the specified storage reader.
   132  func ParseMetadataFromStorage(c *gc.C, stor storage.StorageReader, stream string, expectMirrors bool) []*tools.ToolsMetadata {
   133  	source := storage.NewStorageSimpleStreamsDataSource("test storage reader", stor, "tools")
   134  	params := simplestreams.ValueParams{
   135  		DataType:      tools.ContentDownload,
   136  		ValueTemplate: tools.ToolsMetadata{},
   137  	}
   138  
   139  	const requireSigned = false
   140  	indexPath := simplestreams.UnsignedIndex("v1", 2)
   141  	mirrorsPath := simplestreams.MirrorsPath("v1")
   142  	indexRef, err := simplestreams.GetIndexWithFormat(
   143  		source, indexPath, "index:1.0", mirrorsPath, requireSigned, simplestreams.CloudSpec{}, params)
   144  	c.Assert(err, jc.ErrorIsNil)
   145  
   146  	toolsIndexMetadata := indexRef.Indexes[tools.ToolsContentId(stream)]
   147  	c.Assert(toolsIndexMetadata, gc.NotNil)
   148  
   149  	// Read the products file contents.
   150  	r, err := stor.Get(path.Join("tools", toolsIndexMetadata.ProductsFilePath))
   151  	defer r.Close()
   152  	c.Assert(err, jc.ErrorIsNil)
   153  	data, err := ioutil.ReadAll(r)
   154  	c.Assert(err, jc.ErrorIsNil)
   155  
   156  	url, err := source.URL(toolsIndexMetadata.ProductsFilePath)
   157  	c.Assert(err, jc.ErrorIsNil)
   158  	cloudMetadata, err := simplestreams.ParseCloudMetadata(data, "products:1.0", url, tools.ToolsMetadata{})
   159  	c.Assert(err, jc.ErrorIsNil)
   160  
   161  	toolsMetadataMap := make(map[string]*tools.ToolsMetadata)
   162  	expectedProductIds := make(set.Strings)
   163  	toolsVersions := make(set.Strings)
   164  	for _, mc := range cloudMetadata.Products {
   165  		for _, items := range mc.Items {
   166  			for key, item := range items.Items {
   167  				toolsMetadata := item.(*tools.ToolsMetadata)
   168  				toolsMetadataMap[key] = toolsMetadata
   169  				toolsVersions.Add(key)
   170  				seriesVersion, err := series.SeriesVersion(toolsMetadata.Release)
   171  				if err != nil {
   172  					c.Assert(err, jc.Satisfies, series.IsUnknownSeriesVersionError)
   173  				}
   174  				productId := fmt.Sprintf("com.ubuntu.juju:%s:%s", seriesVersion, toolsMetadata.Arch)
   175  				expectedProductIds.Add(productId)
   176  			}
   177  		}
   178  	}
   179  
   180  	// Make sure index's product IDs are all represented in the products metadata.
   181  	sort.Strings(toolsIndexMetadata.ProductIds)
   182  	c.Assert(toolsIndexMetadata.ProductIds, gc.DeepEquals, expectedProductIds.SortedValues())
   183  
   184  	toolsMetadata := make([]*tools.ToolsMetadata, len(toolsMetadataMap))
   185  	for i, key := range toolsVersions.SortedValues() {
   186  		toolsMetadata[i] = toolsMetadataMap[key]
   187  	}
   188  
   189  	if expectMirrors {
   190  		r, err = stor.Get(path.Join("tools", simplestreams.UnsignedMirror("v1")))
   191  		c.Assert(err, jc.ErrorIsNil)
   192  		defer r.Close()
   193  		data, err = ioutil.ReadAll(r)
   194  		c.Assert(err, jc.ErrorIsNil)
   195  		c.Assert(string(data), jc.Contains, `"mirrors":`)
   196  		c.Assert(string(data), jc.Contains, tools.ToolsContentId(stream))
   197  		c.Assert(err, jc.ErrorIsNil)
   198  	}
   199  	return toolsMetadata
   200  }
   201  
   202  type metadataFile struct {
   203  	path string
   204  	data []byte
   205  }
   206  
   207  func generateMetadata(c *gc.C, stream string, versions ...version.Binary) []metadataFile {
   208  	var metadata = make([]*tools.ToolsMetadata, len(versions))
   209  	for i, vers := range versions {
   210  		basePath := fmt.Sprintf("%s/tools-%s.tar.gz", stream, vers.String())
   211  		metadata[i] = &tools.ToolsMetadata{
   212  			Release: vers.Series,
   213  			Version: vers.Number.String(),
   214  			Arch:    vers.Arch,
   215  			Path:    basePath,
   216  		}
   217  	}
   218  	var streamMetadata = map[string][]*tools.ToolsMetadata{
   219  		stream: metadata,
   220  	}
   221  	index, legacyIndex, products, err := tools.MarshalToolsMetadataJSON(streamMetadata, time.Now())
   222  	c.Assert(err, jc.ErrorIsNil)
   223  	objects := []metadataFile{
   224  		{simplestreams.UnsignedIndex("v1", 2), index},
   225  	}
   226  	if stream == "released" {
   227  		objects = append(objects, metadataFile{simplestreams.UnsignedIndex("v1", 1), legacyIndex})
   228  	}
   229  	for stream, metadata := range products {
   230  		objects = append(objects, metadataFile{tools.ProductMetadataPath(stream), metadata})
   231  	}
   232  	return objects
   233  }
   234  
   235  // UploadToStorage uploads tools and metadata for the specified versions to storage.
   236  func UploadToStorage(c *gc.C, stor storage.Storage, stream string, versions ...version.Binary) map[version.Binary]string {
   237  	uploaded := map[version.Binary]string{}
   238  	if len(versions) == 0 {
   239  		return uploaded
   240  	}
   241  	var err error
   242  	for _, vers := range versions {
   243  		filename := fmt.Sprintf("tools/%s/tools-%s.tar.gz", stream, vers.String())
   244  		// Put a file in images since the dummy storage provider requires a
   245  		// file to exist before the URL can be found. This is to ensure it behaves
   246  		// the same way as MAAS.
   247  		err = stor.Put(filename, strings.NewReader("dummy"), 5)
   248  		c.Assert(err, jc.ErrorIsNil)
   249  		uploaded[vers], err = stor.URL(filename)
   250  		c.Assert(err, jc.ErrorIsNil)
   251  	}
   252  	objects := generateMetadata(c, stream, versions...)
   253  	for _, object := range objects {
   254  		toolspath := path.Join("tools", object.path)
   255  		err = stor.Put(toolspath, bytes.NewReader(object.data), int64(len(object.data)))
   256  		c.Assert(err, jc.ErrorIsNil)
   257  	}
   258  	return uploaded
   259  }
   260  
   261  // UploadToDirectory uploads tools and metadata for the specified versions to dir.
   262  func UploadToDirectory(c *gc.C, stream, dir string, versions ...version.Binary) map[version.Binary]string {
   263  	uploaded := map[version.Binary]string{}
   264  	if len(versions) == 0 {
   265  		return uploaded
   266  	}
   267  	for _, vers := range versions {
   268  		basePath := fmt.Sprintf("%s/tools-%s.tar.gz", stream, vers.String())
   269  		uploaded[vers] = utils.MakeFileURL(fmt.Sprintf("%s/%s", dir, basePath))
   270  	}
   271  	objects := generateMetadata(c, stream, versions...)
   272  	for _, object := range objects {
   273  		path := filepath.Join(dir, object.path)
   274  		dir := filepath.Dir(path)
   275  		if err := os.MkdirAll(dir, 0755); err != nil && !os.IsExist(err) {
   276  			c.Assert(err, jc.ErrorIsNil)
   277  		}
   278  		err := ioutil.WriteFile(path, object.data, 0644)
   279  		c.Assert(err, jc.ErrorIsNil)
   280  	}
   281  	return uploaded
   282  }