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