github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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  
    10  	"github.com/juju/juju/apiserver/common"
    11  	"github.com/juju/juju/apiserver/facade"
    12  	"github.com/juju/juju/apiserver/params"
    13  	"github.com/juju/juju/charmstore"
    14  	"github.com/juju/juju/state"
    15  )
    16  
    17  var logger = loggo.GetLogger("juju.apiserver.charmrevisionupdater")
    18  
    19  func init() {
    20  	common.RegisterStandardFacade("CharmRevisionUpdater", 2, 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  facade.Resources
    33  	authorizer facade.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 facade.Resources,
    42  	authorizer facade.Authorizer,
    43  ) (*CharmRevisionUpdaterAPI, error) {
    44  	if !authorizer.AuthMachineAgent() && !authorizer.AuthModelManager() {
    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  	if err := api.updateLatestRevisions(); err != nil {
    55  		return params.ErrorResult{Error: common.ServerError(err)}, nil
    56  	}
    57  	return params.ErrorResult{}, nil
    58  }
    59  
    60  func (api *CharmRevisionUpdaterAPI) updateLatestRevisions() error {
    61  	// Get the handlers to use.
    62  	handlers, err := createHandlers(api.state)
    63  	if err != nil {
    64  		return err
    65  	}
    66  
    67  	// Look up the information for all the deployed charms. This is the
    68  	// "expensive" part.
    69  	latest, err := retrieveLatestCharmInfo(api.state)
    70  	if err != nil {
    71  		return err
    72  	}
    73  
    74  	// Process the resulting info for each charm.
    75  	for _, info := range latest {
    76  		// First, add a charm placeholder to the model for each.
    77  		if err = api.state.AddStoreCharmPlaceholder(info.LatestURL()); err != nil {
    78  			return err
    79  		}
    80  
    81  		// Then run through the handlers.
    82  		serviceID := info.service.ApplicationTag()
    83  		for _, handler := range handlers {
    84  			if err := handler.HandleLatest(serviceID, info.CharmInfo); err != nil {
    85  				return err
    86  			}
    87  		}
    88  	}
    89  
    90  	return nil
    91  }
    92  
    93  // NewCharmStoreClient instantiates a new charm store repository.  Exported so
    94  // we can change it during testing.
    95  var NewCharmStoreClient = func(st *state.State) (charmstore.Client, error) {
    96  	return charmstore.NewCachingClient(state.MacaroonCache{st}, nil)
    97  }
    98  
    99  type latestCharmInfo struct {
   100  	charmstore.CharmInfo
   101  	service *state.Application
   102  }
   103  
   104  // retrieveLatestCharmInfo looks up the charm store to return the charm URLs for the
   105  // latest revision of the deployed charms.
   106  func retrieveLatestCharmInfo(st *state.State) ([]latestCharmInfo, error) {
   107  	// First get the uuid for the environment to use when querying the charm store.
   108  	env, err := st.Model()
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  
   113  	services, err := st.AllApplications()
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  
   118  	client, err := NewCharmStoreClient(st)
   119  	if err != nil {
   120  		return nil, errors.Trace(err)
   121  	}
   122  
   123  	var charms []charmstore.CharmID
   124  	var resultsIndexedServices []*state.Application
   125  	for _, service := range services {
   126  		curl, _ := service.CharmURL()
   127  		if curl.Schema == "local" {
   128  			// Version checking for charms from local repositories is not
   129  			// currently supported, since we don't yet support passing in
   130  			// a path to the local repo. This may change if the need arises.
   131  			continue
   132  		}
   133  
   134  		cid := charmstore.CharmID{
   135  			URL:     curl,
   136  			Channel: service.Channel(),
   137  		}
   138  		charms = append(charms, cid)
   139  		resultsIndexedServices = append(resultsIndexedServices, service)
   140  	}
   141  
   142  	metadata := map[string]string{
   143  		"environment_uuid": env.UUID(),
   144  		"cloud":            env.Cloud(),
   145  		"cloud_region":     env.CloudRegion(),
   146  	}
   147  	cloud, err := st.Cloud(env.Cloud())
   148  	if err != nil {
   149  		metadata["provider"] = "unknown"
   150  	} else {
   151  		metadata["provider"] = cloud.Type
   152  	}
   153  	results, err := charmstore.LatestCharmInfo(client, charms, metadata)
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  
   158  	var latest []latestCharmInfo
   159  	for i, result := range results {
   160  		if result.Error != nil {
   161  			logger.Errorf("retrieving charm info for %s: %v", charms[i].URL, result.Error)
   162  			continue
   163  		}
   164  		service := resultsIndexedServices[i]
   165  		latest = append(latest, latestCharmInfo{
   166  			CharmInfo: result.CharmInfo,
   167  			service:   service,
   168  		})
   169  	}
   170  	return latest, nil
   171  }