github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/environs/gui/simplestreams.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package gui 5 6 import ( 7 "fmt" 8 "sort" 9 10 "github.com/juju/errors" 11 "github.com/juju/utils" 12 "github.com/juju/version" 13 14 "github.com/juju/juju/environs/simplestreams" 15 "github.com/juju/juju/juju" 16 jujuversion "github.com/juju/juju/version" 17 ) 18 19 const ( 20 // DefaultBaseURL holds the default simplestreams data source URL from 21 // where to retrieve Juju GUI archives. 22 DefaultBaseURL = "https://streams.canonical.com/juju/gui" 23 // ReleasedStream and DevelStreams hold stream names to use when fetching 24 // Juju GUI archives. 25 ReleasedStream = "released" 26 DevelStream = "devel" 27 28 downloadType = "content-download" 29 sourceDescription = "gui simplestreams" 30 streamsVersion = "v1" 31 ) 32 33 func init() { 34 simplestreams.RegisterStructTags(Metadata{}) 35 } 36 37 // DataSource creates and returns a new simplestreams signed data source for 38 // fetching Juju GUI archives, at the given URL. 39 func NewDataSource(baseURL string) simplestreams.DataSource { 40 requireSigned := true 41 return simplestreams.NewURLSignedDataSource( 42 sourceDescription, 43 baseURL, 44 juju.JujuPublicKey, 45 utils.VerifySSLHostnames, 46 simplestreams.DEFAULT_CLOUD_DATA, 47 requireSigned) 48 } 49 50 // FetchMetadata fetches and returns Juju GUI metadata from simplestreams, 51 // sorted by version descending. 52 func FetchMetadata(stream string, sources ...simplestreams.DataSource) ([]*Metadata, error) { 53 params := simplestreams.GetMetadataParams{ 54 StreamsVersion: streamsVersion, 55 LookupConstraint: &constraint{ 56 LookupParams: simplestreams.LookupParams{Stream: stream}, 57 majorVersion: jujuversion.Current.Major, 58 }, 59 ValueParams: simplestreams.ValueParams{ 60 DataType: downloadType, 61 MirrorContentId: contentId(stream), 62 FilterFunc: appendArchives, 63 ValueTemplate: Metadata{}, 64 }, 65 } 66 items, _, err := simplestreams.GetMetadata(sources, params) 67 if err != nil { 68 return nil, errors.Annotate(err, "error fetching simplestreams metadata") 69 } 70 allMeta := make([]*Metadata, len(items)) 71 for i, item := range items { 72 allMeta[i] = item.(*Metadata) 73 } 74 sort.Sort(byVersion(allMeta)) 75 return allMeta, nil 76 } 77 78 // Metadata is the type used to retrieve GUI archive metadata information from 79 // simplestream. Tags for this structure are registered in init(). 80 type Metadata struct { 81 Size int64 `json:"size"` 82 SHA256 string `json:"sha256"` 83 Path string `json:"path"` 84 85 JujuMajorVersion int `json:"juju-version"` 86 StringVersion string `json:"version"` 87 88 Version version.Number `json:"-"` 89 FullPath string `json:"-"` 90 Source simplestreams.DataSource `json:"-"` 91 } 92 93 // byVersion is used to sort GUI metadata by version, most recent first. 94 type byVersion []*Metadata 95 96 // Len implements sort.Interface. 97 func (b byVersion) Len() int { return len(b) } 98 99 // Swap implements sort.Interface. 100 func (b byVersion) Swap(i, j int) { b[i], b[j] = b[j], b[i] } 101 102 // Less implements sort.Interface. 103 func (b byVersion) Less(i, j int) bool { return b[i].Version.Compare(b[j].Version) > 0 } 104 105 // constraint is used as simplestreams.LookupConstraint when retrieving Juju 106 // GUI metadata information. 107 type constraint struct { 108 simplestreams.LookupParams 109 majorVersion int 110 } 111 112 // IndexIds generates a string array representing index ids formed similarly to 113 // an ISCSI qualified name (IQN). 114 func (c *constraint) IndexIds() []string { 115 return []string{contentId(c.Stream)} 116 } 117 118 // ProductIds generates a string array representing product ids formed 119 // similarly to an ISCSI qualified name (IQN). 120 func (c *constraint) ProductIds() ([]string, error) { 121 return []string{"com.canonical.streams:gui"}, nil 122 } 123 124 // contentId returns the GUI content id in simplestreams for the given stream. 125 func contentId(stream string) string { 126 return fmt.Sprintf("com.canonical.streams:%s:gui", stream) 127 } 128 129 // appendArchives collects all matching Juju GUI archive metadata information. 130 func appendArchives( 131 source simplestreams.DataSource, 132 matchingItems []interface{}, 133 items map[string]interface{}, 134 cons simplestreams.LookupConstraint, 135 ) ([]interface{}, error) { 136 var majorVersion int 137 if guiConstraint, ok := cons.(*constraint); ok { 138 majorVersion = guiConstraint.majorVersion 139 } 140 for _, item := range items { 141 meta := item.(*Metadata) 142 if majorVersion != 0 && majorVersion != meta.JujuMajorVersion { 143 continue 144 } 145 fullPath, err := source.URL(meta.Path) 146 if err != nil { 147 return nil, errors.Annotate(err, "cannot retrieve metadata full path") 148 } 149 meta.FullPath = fullPath 150 vers, err := version.Parse(meta.StringVersion) 151 if err != nil { 152 return nil, errors.Annotate(err, "cannot parse metadata version") 153 } 154 meta.Version = vers 155 meta.Source = source 156 matchingItems = append(matchingItems, meta) 157 } 158 return matchingItems, nil 159 }