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