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 }