github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/apiserver/charmrevisionupdater/updater.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package charmrevisionupdater
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/loggo"
     9  	"gopkg.in/juju/charm.v4"
    10  
    11  	"github.com/juju/juju/apiserver/common"
    12  	"github.com/juju/juju/apiserver/params"
    13  	"github.com/juju/juju/state"
    14  )
    15  
    16  var logger = loggo.GetLogger("juju.apiserver.charmrevisionupdater")
    17  
    18  func init() {
    19  	common.RegisterStandardFacade("CharmRevisionUpdater", 0, NewCharmRevisionUpdaterAPI)
    20  }
    21  
    22  // CharmRevisionUpdater defines the methods on the charmrevisionupdater API end point.
    23  type CharmRevisionUpdater interface {
    24  	UpdateLatestRevisions() (params.ErrorResult, error)
    25  }
    26  
    27  // CharmRevisionUpdaterAPI implements the CharmRevisionUpdater interface and is the concrete
    28  // implementation of the api end point.
    29  type CharmRevisionUpdaterAPI struct {
    30  	state      *state.State
    31  	resources  *common.Resources
    32  	authorizer common.Authorizer
    33  }
    34  
    35  var _ CharmRevisionUpdater = (*CharmRevisionUpdaterAPI)(nil)
    36  
    37  // NewCharmRevisionUpdaterAPI creates a new server-side charmrevisionupdater API end point.
    38  func NewCharmRevisionUpdaterAPI(
    39  	st *state.State,
    40  	resources *common.Resources,
    41  	authorizer common.Authorizer,
    42  ) (*CharmRevisionUpdaterAPI, error) {
    43  	if !authorizer.AuthMachineAgent() && !authorizer.AuthEnvironManager() {
    44  		return nil, common.ErrPerm
    45  	}
    46  	return &CharmRevisionUpdaterAPI{
    47  		state: st, resources: resources, authorizer: authorizer}, nil
    48  }
    49  
    50  // UpdateLatestRevisions retrieves the latest revision information from the charm store for all deployed charms
    51  // and records this information in state.
    52  func (api *CharmRevisionUpdaterAPI) UpdateLatestRevisions() (params.ErrorResult, error) {
    53  	// First get the uuid for the environment to use when querying the charm store.
    54  	env, err := api.state.Environment()
    55  	if err != nil {
    56  		return params.ErrorResult{Error: common.ServerError(err)}, nil
    57  	}
    58  	uuid := env.UUID()
    59  
    60  	deployedCharms, err := fetchAllDeployedCharms(api.state)
    61  	if err != nil {
    62  		return params.ErrorResult{Error: common.ServerError(err)}, nil
    63  	}
    64  	// Look up the revision information for all the deployed charms.
    65  	curls, err := retrieveLatestCharmInfo(deployedCharms, uuid)
    66  	if err != nil {
    67  		return params.ErrorResult{Error: common.ServerError(err)}, nil
    68  	}
    69  	// Add the charms and latest revision info to state as charm placeholders.
    70  	for _, curl := range curls {
    71  		if err = api.state.AddStoreCharmPlaceholder(curl); err != nil {
    72  			return params.ErrorResult{Error: common.ServerError(err)}, nil
    73  		}
    74  	}
    75  	return params.ErrorResult{}, nil
    76  }
    77  
    78  // fetchAllDeployedCharms returns a map from service name to service
    79  // and a map from service name to unit name to unit.
    80  func fetchAllDeployedCharms(st *state.State) (map[string]*charm.URL, error) {
    81  	deployedCharms := make(map[string]*charm.URL)
    82  	services, err := st.AllServices()
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	for _, s := range services {
    87  		url, _ := s.CharmURL()
    88  		// Record the basic charm information so it can be bulk processed later to
    89  		// get the available revision numbers from the repo.
    90  		baseCharm := url.WithRevision(-1)
    91  		deployedCharms[baseCharm.String()] = baseCharm
    92  	}
    93  	return deployedCharms, nil
    94  }
    95  
    96  // retrieveLatestCharmInfo looks up the charm store to return the charm URLs for the
    97  // latest revision of the deployed charms.
    98  func retrieveLatestCharmInfo(deployedCharms map[string]*charm.URL, uuid string) ([]*charm.URL, error) {
    99  	var curls []*charm.URL
   100  	for _, curl := range deployedCharms {
   101  		if curl.Schema == "local" {
   102  			// Version checking for charms from local repositories is not
   103  			// currently supported, since we don't yet support passing in
   104  			// a path to the local repo. This may change if the need arises.
   105  			continue
   106  		}
   107  		curls = append(curls, curl)
   108  	}
   109  
   110  	// Do a bulk call to get the revision info for all charms.
   111  	logger.Infof("retrieving revision information for %d charms", len(curls))
   112  	store := charm.Store.WithJujuAttrs("environment_uuid=" + uuid)
   113  	revInfo, err := store.Latest(curls...)
   114  	if err != nil {
   115  		err = errors.Annotate(err, "finding charm revision info")
   116  		logger.Infof(err.Error())
   117  		return nil, err
   118  	}
   119  	var latestCurls []*charm.URL
   120  	for i, info := range revInfo {
   121  		curl := curls[i]
   122  		if info.Err == nil {
   123  			latestCurls = append(latestCurls, curl.WithRevision(info.Revision))
   124  		} else {
   125  			logger.Errorf("retrieving charm info for %s: %v", curl, info.Err)
   126  		}
   127  	}
   128  	return latestCurls, nil
   129  }