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