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