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 }