github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/agent/payloadshookcontext/unitfacade.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package payloadshookcontext
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/loggo"
     9  	"gopkg.in/juju/names.v2"
    10  
    11  	"github.com/juju/juju/apiserver/common"
    12  	"github.com/juju/juju/apiserver/params"
    13  	"github.com/juju/juju/payload"
    14  	"github.com/juju/juju/payload/api"
    15  	"github.com/juju/juju/state"
    16  )
    17  
    18  var logger = loggo.GetLogger("juju.apiserver.payloadshookcontext")
    19  
    20  // NewHookContextFacade returns a new payloads hook context facade for
    21  // the State and Unit given. It is used for facade registration.
    22  func NewHookContextFacade(st *state.State, unit *state.Unit) (interface{}, error) {
    23  	up, err := st.UnitPayloads(unit)
    24  	if err != nil {
    25  		return nil, errors.Trace(err)
    26  	}
    27  	return NewUnitFacade(up), nil
    28  }
    29  
    30  // UnitPayloadBackend exposes the State functionality for a unit's payloads.
    31  type UnitPayloadBackend interface {
    32  	// Track tracks a payload for the unit and info.
    33  	Track(info payload.Payload) error
    34  
    35  	// List returns information on the payload with the id on the unit.
    36  	List(ids ...string) ([]payload.Result, error)
    37  
    38  	// Settatus sets the status for the payload with the given id on the unit.
    39  	SetStatus(id, status string) error
    40  
    41  	// LookUp returns the payload ID for the given name/rawID pair.
    42  	LookUp(name, rawID string) (string, error)
    43  
    44  	// Untrack removes the information for the payload with the given id.
    45  	Untrack(id string) error
    46  }
    47  
    48  // UnitFacade serves payload-specific API methods.
    49  type UnitFacade struct {
    50  	backend UnitPayloadBackend
    51  }
    52  
    53  // NewUnitFacade builds a new facade for the given backend.
    54  func NewUnitFacade(backend UnitPayloadBackend) *UnitFacade {
    55  	return &UnitFacade{backend: backend}
    56  }
    57  
    58  // Track stores a payload to be tracked in state.
    59  func (uf UnitFacade) Track(args params.TrackPayloadArgs) (params.PayloadResults, error) {
    60  	logger.Debugf("tracking %d payloads from API", len(args.Payloads))
    61  
    62  	var r params.PayloadResults
    63  	for _, apiPayload := range args.Payloads {
    64  		pl, err := api.API2Payload(apiPayload)
    65  		if err != nil {
    66  			return r, errors.Trace(err)
    67  		}
    68  		logger.Debugf("tracking payload from API: %#v", pl)
    69  
    70  		id, err := uf.track(pl.Payload)
    71  		res := newPayloadResult(id, err)
    72  		r.Results = append(r.Results, res)
    73  	}
    74  	return r, nil
    75  }
    76  
    77  func (uf UnitFacade) track(pl payload.Payload) (string, error) {
    78  	if err := uf.backend.Track(pl); err != nil {
    79  		return "", errors.Trace(err)
    80  	}
    81  	id, err := uf.backend.LookUp(pl.Name, pl.ID)
    82  	if err != nil {
    83  		return "", errors.Trace(err)
    84  	}
    85  	return id, nil
    86  }
    87  
    88  // List builds the list of payload being tracked for
    89  // the given unit and IDs. If no IDs are provided then all tracked
    90  // payloads for the unit are returned.
    91  func (uf UnitFacade) List(args params.Entities) (params.PayloadResults, error) {
    92  	if len(args.Entities) == 0 {
    93  		return uf.listAll()
    94  	}
    95  
    96  	var ids []string
    97  	for _, entity := range args.Entities {
    98  		id, err := api.API2ID(entity.Tag)
    99  		if err != nil {
   100  			return params.PayloadResults{}, errors.Trace(err)
   101  		}
   102  		ids = append(ids, id)
   103  	}
   104  
   105  	results, err := uf.backend.List(ids...)
   106  	if err != nil {
   107  		return params.PayloadResults{}, errors.Trace(err)
   108  	}
   109  
   110  	var r params.PayloadResults
   111  	for _, result := range results {
   112  		res := Result2api(result)
   113  		r.Results = append(r.Results, res)
   114  	}
   115  	return r, nil
   116  }
   117  
   118  func (uf UnitFacade) listAll() (params.PayloadResults, error) {
   119  	var r params.PayloadResults
   120  
   121  	results, err := uf.backend.List()
   122  	if err != nil {
   123  		return r, errors.Trace(err)
   124  	}
   125  
   126  	for _, result := range results {
   127  		pl := result.Payload
   128  		id, err := uf.backend.LookUp(pl.Name, pl.ID)
   129  		if err != nil {
   130  			logger.Errorf("failed to look up ID for %q: %v", pl.FullID(), err)
   131  			id = ""
   132  		}
   133  		apipl := api.Payload2api(*pl)
   134  
   135  		res := newPayloadResult(id, nil)
   136  		res.Payload = &apipl
   137  		r.Results = append(r.Results, res)
   138  	}
   139  	return r, nil
   140  }
   141  
   142  // LookUp identifies the payload with the provided name and raw ID.
   143  func (uf UnitFacade) LookUp(args params.LookUpPayloadArgs) (params.PayloadResults, error) {
   144  	var r params.PayloadResults
   145  	for _, arg := range args.Args {
   146  		id, err := uf.backend.LookUp(arg.Name, arg.ID)
   147  		res := newPayloadResult(id, err)
   148  		r.Results = append(r.Results, res)
   149  	}
   150  	return r, nil
   151  }
   152  
   153  // SetStatus sets the raw status of a payload.
   154  func (uf UnitFacade) SetStatus(args params.SetPayloadStatusArgs) (params.PayloadResults, error) {
   155  	var r params.PayloadResults
   156  	for _, arg := range args.Args {
   157  		id, err := api.API2ID(arg.Tag)
   158  		if err != nil {
   159  			return r, errors.Trace(err)
   160  		}
   161  
   162  		err = uf.backend.SetStatus(id, arg.Status)
   163  		res := newPayloadResult(id, err)
   164  		r.Results = append(r.Results, res)
   165  	}
   166  	return r, nil
   167  }
   168  
   169  // Untrack marks the identified payload as no longer being tracked.
   170  func (uf UnitFacade) Untrack(args params.Entities) (params.PayloadResults, error) {
   171  	var r params.PayloadResults
   172  	for _, entity := range args.Entities {
   173  		id, err := api.API2ID(entity.Tag)
   174  		if err != nil {
   175  			return r, errors.Trace(err)
   176  		}
   177  
   178  		err = uf.backend.Untrack(id)
   179  		res := newPayloadResult(id, err)
   180  		r.Results = append(r.Results, res)
   181  	}
   182  	return r, nil
   183  }
   184  
   185  // newPayloadResult builds a new PayloadResult from the provided tag
   186  // and error. NotFound is also set based on the error.
   187  func newPayloadResult(id string, err error) params.PayloadResult {
   188  	result := payload.Result{
   189  		ID:       id,
   190  		Payload:  nil,
   191  		NotFound: errors.IsNotFound(err),
   192  		Error:    err,
   193  	}
   194  	return Result2api(result)
   195  }
   196  
   197  // Result2api converts the payload.Result into a PayloadResult.
   198  func Result2api(result payload.Result) params.PayloadResult {
   199  	res := params.PayloadResult{
   200  		NotFound: result.NotFound,
   201  	}
   202  
   203  	if result.ID != "" {
   204  		res.Tag = names.NewPayloadTag(result.ID).String()
   205  	}
   206  
   207  	if result.Payload != nil {
   208  		pl := api.Payload2api(*result.Payload)
   209  		res.Payload = &pl
   210  	}
   211  
   212  	if result.Error != nil {
   213  		res.Error = common.ServerError(result.Error)
   214  	}
   215  
   216  	return res
   217  }