github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/environs/tools/marshal.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // The tools package supports locating, parsing, and filtering Ubuntu tools metadata in simplestreams format.
     5  // See http://launchpad.net/simplestreams and in particular the doc/README file in that project for more information
     6  // about the file formats.
     7  package tools
     8  
     9  import (
    10  	"encoding/json"
    11  	"fmt"
    12  	"time"
    13  
    14  	"github.com/juju/utils/set"
    15  
    16  	"github.com/juju/juju/environs/simplestreams"
    17  	"github.com/juju/juju/version"
    18  )
    19  
    20  // ToolsContentId returns the tools content id for the given stream.
    21  func ToolsContentId(stream string) string {
    22  	return fmt.Sprintf("com.ubuntu.juju:%s:tools", stream)
    23  }
    24  
    25  // ProductMetadataPath returns the tools product metadata path for the given stream.
    26  func ProductMetadataPath(stream string) string {
    27  	return fmt.Sprintf("streams/v1/com.ubuntu.juju-%s-tools.json", stream)
    28  }
    29  
    30  // MarshalToolsMetadataJSON marshals tools metadata to index and products JSON.
    31  // updated is the time at which the JSON file was updated.
    32  func MarshalToolsMetadataJSON(metadata map[string][]*ToolsMetadata, updated time.Time) (index, legacyIndex []byte, products map[string][]byte, err error) {
    33  	if index, legacyIndex, err = marshalToolsMetadataIndexJSON(metadata, updated); err != nil {
    34  		return nil, nil, nil, err
    35  	}
    36  	if products, err = MarshalToolsMetadataProductsJSON(metadata, updated); err != nil {
    37  		return nil, nil, nil, err
    38  	}
    39  	return index, legacyIndex, products, err
    40  }
    41  
    42  // marshalToolsMetadataIndexJSON marshals tools metadata to index JSON.
    43  // updated is the time at which the JSON file was updated.
    44  func marshalToolsMetadataIndexJSON(streamMetadata map[string][]*ToolsMetadata, updated time.Time) (out, outlegacy []byte, err error) {
    45  	var indices simplestreams.Indices
    46  	indices.Updated = updated.Format(time.RFC1123Z)
    47  	indices.Format = simplestreams.IndexFormat
    48  	indices.Indexes = make(map[string]*simplestreams.IndexMetadata, len(streamMetadata))
    49  
    50  	var indicesLegacy simplestreams.Indices
    51  	indicesLegacy.Updated = updated.Format(time.RFC1123Z)
    52  	indicesLegacy.Format = simplestreams.IndexFormat
    53  
    54  	for stream, metadata := range streamMetadata {
    55  		var productIds []string
    56  		for _, t := range metadata {
    57  			id, err := t.productId()
    58  			if err != nil {
    59  				if version.IsUnknownSeriesVersionError(err) {
    60  					logger.Infof("ignoring tools metadata with unknown series %q", t.Release)
    61  					continue
    62  				}
    63  				return nil, nil, err
    64  			}
    65  			productIds = append(productIds, id)
    66  		}
    67  		indexMetadata := &simplestreams.IndexMetadata{
    68  			Updated:          indices.Updated,
    69  			Format:           simplestreams.ProductFormat,
    70  			DataType:         ContentDownload,
    71  			ProductsFilePath: ProductMetadataPath(stream),
    72  			ProductIds:       set.NewStrings(productIds...).SortedValues(),
    73  		}
    74  		indices.Indexes[ToolsContentId(stream)] = indexMetadata
    75  		if stream == ReleasedStream {
    76  			indicesLegacy.Indexes = make(map[string]*simplestreams.IndexMetadata, 1)
    77  			indicesLegacy.Indexes[ToolsContentId(stream)] = indexMetadata
    78  		}
    79  	}
    80  	out, err = json.MarshalIndent(&indices, "", "    ")
    81  	if len(indicesLegacy.Indexes) == 0 {
    82  		return out, nil, err
    83  	}
    84  	outlegacy, err = json.MarshalIndent(&indicesLegacy, "", "    ")
    85  	if err != nil {
    86  		return nil, nil, err
    87  	}
    88  	return out, outlegacy, nil
    89  }
    90  
    91  // MarshalToolsMetadataProductsJSON marshals tools metadata to products JSON.
    92  // updated is the time at which the JSON file was updated.
    93  func MarshalToolsMetadataProductsJSON(
    94  	streamMetadata map[string][]*ToolsMetadata, updated time.Time,
    95  ) (out map[string][]byte, err error) {
    96  
    97  	out = make(map[string][]byte, len(streamMetadata))
    98  	for stream, metadata := range streamMetadata {
    99  		var cloud simplestreams.CloudMetadata
   100  		cloud.Updated = updated.Format(time.RFC1123Z)
   101  		cloud.Format = simplestreams.ProductFormat
   102  		cloud.ContentId = ToolsContentId(stream)
   103  		cloud.Products = make(map[string]simplestreams.MetadataCatalog)
   104  		itemsversion := updated.Format("20060102") // YYYYMMDD
   105  		for _, t := range metadata {
   106  			id, err := t.productId()
   107  			if err != nil {
   108  				if version.IsUnknownSeriesVersionError(err) {
   109  					continue
   110  				}
   111  				return nil, err
   112  			}
   113  			itemid := fmt.Sprintf("%s-%s-%s", t.Version, t.Release, t.Arch)
   114  			if catalog, ok := cloud.Products[id]; ok {
   115  				catalog.Items[itemsversion].Items[itemid] = t
   116  			} else {
   117  				catalog = simplestreams.MetadataCatalog{
   118  					Arch:    t.Arch,
   119  					Version: t.Version,
   120  					Items: map[string]*simplestreams.ItemCollection{
   121  						itemsversion: {
   122  							Items: map[string]interface{}{itemid: t},
   123  						},
   124  					},
   125  				}
   126  				cloud.Products[id] = catalog
   127  			}
   128  		}
   129  		if out[stream], err = json.MarshalIndent(&cloud, "", "    "); err != nil {
   130  			return nil, err
   131  		}
   132  	}
   133  	return out, nil
   134  }