github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/apiserver/imagemetadata/metadata.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package imagemetadata
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  
    10  	"github.com/juju/loggo"
    11  	"github.com/juju/utils/series"
    12  
    13  	"github.com/juju/errors"
    14  	"github.com/juju/juju/apiserver/common"
    15  	"github.com/juju/juju/apiserver/params"
    16  	"github.com/juju/juju/environs"
    17  	envmetadata "github.com/juju/juju/environs/imagemetadata"
    18  	"github.com/juju/juju/environs/simplestreams"
    19  	"github.com/juju/juju/state"
    20  	"github.com/juju/juju/state/cloudimagemetadata"
    21  )
    22  
    23  var logger = loggo.GetLogger("juju.apiserver.imagemetadata")
    24  
    25  func init() {
    26  	common.RegisterStandardFacade("ImageMetadata", 1, NewAPI)
    27  }
    28  
    29  // API is the concrete implementation of the api end point
    30  // for loud image metadata manipulations.
    31  type API struct {
    32  	metadata   metadataAcess
    33  	authorizer common.Authorizer
    34  }
    35  
    36  // createAPI returns a new image metadata API facade.
    37  func createAPI(
    38  	st metadataAcess,
    39  	resources *common.Resources,
    40  	authorizer common.Authorizer,
    41  ) (*API, error) {
    42  	if !authorizer.AuthClient() && !authorizer.AuthEnvironManager() {
    43  		return nil, common.ErrPerm
    44  	}
    45  
    46  	return &API{
    47  		metadata:   st,
    48  		authorizer: authorizer,
    49  	}, nil
    50  }
    51  
    52  // NewAPI returns a new cloud image metadata API facade.
    53  func NewAPI(
    54  	st *state.State,
    55  	resources *common.Resources,
    56  	authorizer common.Authorizer,
    57  ) (*API, error) {
    58  	return createAPI(getState(st), resources, authorizer)
    59  }
    60  
    61  // List returns all found cloud image metadata that satisfy
    62  // given filter.
    63  // Returned list contains metadata for custom images first, then public.
    64  func (api *API) List(filter params.ImageMetadataFilter) (params.ListCloudImageMetadataResult, error) {
    65  	found, err := api.metadata.FindMetadata(cloudimagemetadata.MetadataFilter{
    66  		Region:          filter.Region,
    67  		Series:          filter.Series,
    68  		Arches:          filter.Arches,
    69  		Stream:          filter.Stream,
    70  		VirtType:        filter.VirtType,
    71  		RootStorageType: filter.RootStorageType,
    72  	})
    73  	if err != nil {
    74  		return params.ListCloudImageMetadataResult{}, common.ServerError(err)
    75  	}
    76  
    77  	var all []params.CloudImageMetadata
    78  	addAll := func(ms []cloudimagemetadata.Metadata) {
    79  		for _, m := range ms {
    80  			all = append(all, parseMetadataToParams(m))
    81  		}
    82  	}
    83  
    84  	// First return metadata for custom images, then public.
    85  	// No other source for cloud images should exist at the moment.
    86  	// Once new source is identified, the order of returned metadata
    87  	// may need to be changed.
    88  	addAll(found[cloudimagemetadata.Custom])
    89  	addAll(found[cloudimagemetadata.Public])
    90  
    91  	return params.ListCloudImageMetadataResult{Result: all}, nil
    92  }
    93  
    94  // Save stores given cloud image metadata.
    95  // It supports bulk calls.
    96  func (api *API) Save(metadata params.MetadataSaveParams) (params.ErrorResults, error) {
    97  	all := make([]params.ErrorResult, len(metadata.Metadata))
    98  	for i, one := range metadata.Metadata {
    99  		err := api.metadata.SaveMetadata(parseMetadataFromParams(one))
   100  		all[i] = params.ErrorResult{Error: common.ServerError(err)}
   101  	}
   102  	return params.ErrorResults{Results: all}, nil
   103  }
   104  
   105  func parseMetadataToParams(p cloudimagemetadata.Metadata) params.CloudImageMetadata {
   106  	result := params.CloudImageMetadata{
   107  		ImageId:         p.ImageId,
   108  		Stream:          p.Stream,
   109  		Region:          p.Region,
   110  		Series:          p.Series,
   111  		Arch:            p.Arch,
   112  		VirtType:        p.VirtType,
   113  		RootStorageType: p.RootStorageType,
   114  		RootStorageSize: p.RootStorageSize,
   115  		Source:          string(p.Source),
   116  	}
   117  	return result
   118  }
   119  
   120  func parseMetadataFromParams(p params.CloudImageMetadata) cloudimagemetadata.Metadata {
   121  
   122  	parseSource := func(s string) cloudimagemetadata.SourceType {
   123  		switch cloudimagemetadata.SourceType(strings.ToLower(s)) {
   124  		case cloudimagemetadata.Public:
   125  			return cloudimagemetadata.Public
   126  		case cloudimagemetadata.Custom:
   127  			return cloudimagemetadata.Custom
   128  		default:
   129  			panic(fmt.Sprintf("unknown cloud image metadata source %q", s))
   130  		}
   131  	}
   132  
   133  	result := cloudimagemetadata.Metadata{
   134  		cloudimagemetadata.MetadataAttributes{
   135  			Stream:          p.Stream,
   136  			Region:          p.Region,
   137  			Series:          p.Series,
   138  			Arch:            p.Arch,
   139  			VirtType:        p.VirtType,
   140  			RootStorageType: p.RootStorageType,
   141  			RootStorageSize: p.RootStorageSize,
   142  			Source:          parseSource(p.Source),
   143  		},
   144  		p.ImageId,
   145  	}
   146  	if p.Stream == "" {
   147  		result.Stream = "released"
   148  	}
   149  	return result
   150  }
   151  
   152  // UpdateFromPublishedImages retrieves currently published image metadata and
   153  // updates stored ones accordingly.
   154  func (api *API) UpdateFromPublishedImages() error {
   155  	published, err := api.retrievePublished()
   156  	if err != nil {
   157  		return errors.Annotatef(err, "getting published images metadata")
   158  	}
   159  	err = api.saveAll(published)
   160  	return errors.Annotatef(err, "saving published images metadata")
   161  }
   162  
   163  func (api *API) retrievePublished() ([]*envmetadata.ImageMetadata, error) {
   164  	// Get environ
   165  	envCfg, err := api.metadata.EnvironConfig()
   166  	env, err := environs.New(envCfg)
   167  	if err != nil {
   168  		return nil, errors.Trace(err)
   169  	}
   170  
   171  	// Get all images metadata sources for this environ.
   172  	sources, err := environs.ImageMetadataSources(env)
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  
   177  	// We want all metadata.
   178  	cons := envmetadata.NewImageConstraint(simplestreams.LookupParams{})
   179  	metadata, _, err := envmetadata.Fetch(sources, cons, false)
   180  	if err != nil {
   181  		return nil, err
   182  	}
   183  	return metadata, nil
   184  }
   185  
   186  func (api *API) saveAll(published []*envmetadata.ImageMetadata) error {
   187  	// Store converted metadata.
   188  	// Note that whether the metadata actually needs
   189  	// to be stored will be determined within this call.
   190  	errs, err := api.Save(convertToParams(published))
   191  	if err != nil {
   192  		return errors.Annotatef(err, "saving published images metadata")
   193  	}
   194  	return processErrors(errs.Results)
   195  }
   196  
   197  // convertToParams converts environment-specific images metadata to structured metadata format.
   198  var convertToParams = func(published []*envmetadata.ImageMetadata) params.MetadataSaveParams {
   199  	metadata := make([]params.CloudImageMetadata, len(published))
   200  	for i, p := range published {
   201  		metadata[i] = params.CloudImageMetadata{
   202  			Source:          "public",
   203  			ImageId:         p.Id,
   204  			Stream:          p.Stream,
   205  			Region:          p.RegionName,
   206  			Arch:            p.Arch,
   207  			VirtType:        p.VirtType,
   208  			RootStorageType: p.Storage,
   209  		}
   210  		// Translate version (eg.14.04) to a series (eg. "trusty")
   211  		s, err := series.VersionSeries(p.Version)
   212  		if err != nil {
   213  			logger.Warningf("could not determine series for image id %s: %v", p.Id, err)
   214  			continue
   215  		}
   216  		metadata[i].Series = s
   217  	}
   218  
   219  	return params.MetadataSaveParams{Metadata: metadata}
   220  }
   221  
   222  func processErrors(errs []params.ErrorResult) error {
   223  	msgs := []string{}
   224  	for _, e := range errs {
   225  		if e.Error != nil && e.Error.Message != "" {
   226  			msgs = append(msgs, e.Error.Message)
   227  		}
   228  	}
   229  	if len(msgs) != 0 {
   230  		return errors.Errorf("saving some image metadata:\n%v", strings.Join(msgs, "\n"))
   231  	}
   232  	return nil
   233  }