github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/payload/state/unit.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/loggo"
     9  	"github.com/juju/utils"
    10  
    11  	"github.com/juju/juju/payload"
    12  	"github.com/juju/juju/payload/persistence"
    13  )
    14  
    15  var logger = loggo.GetLogger("juju.payload.state")
    16  
    17  // TODO(ericsnow) We need a worker to clean up dying payloads.
    18  
    19  // The persistence methods needed for payloads in state.
    20  type payloadsPersistence interface {
    21  	Track(id string, info payload.Payload) (bool, error)
    22  	// SetStatus updates the status for a payload.
    23  	SetStatus(id, status string) (bool, error)
    24  	List(ids ...string) ([]payload.Payload, []string, error)
    25  	ListAll() ([]payload.Payload, error)
    26  	LookUp(name, rawID string) (string, error)
    27  	Untrack(id string) (bool, error)
    28  }
    29  
    30  // UnitPayloads provides the functionality related to a unit's
    31  // payloads, as needed by state.
    32  type UnitPayloads struct {
    33  	// Persist is the persistence layer that will be used.
    34  	Persist payloadsPersistence
    35  
    36  	// Unit identifies the unit associated with the payloads. This
    37  	// is the "unit ID" of the targeted unit.
    38  	Unit string
    39  
    40  	// Machine identifies the unit's machine. This is the "machine ID"
    41  	// of the machine on which the unit is running.
    42  	Machine string
    43  
    44  	newID func() (string, error)
    45  }
    46  
    47  // NewUnitPayloads builds a UnitPayloads for a unit.
    48  func NewUnitPayloads(st persistence.PersistenceBase, unit, machine string) *UnitPayloads {
    49  	persist := persistence.NewPersistence(st, unit)
    50  	return &UnitPayloads{
    51  		Persist: persist,
    52  		Unit:    unit,
    53  		Machine: machine,
    54  		newID:   newID,
    55  	}
    56  }
    57  
    58  func newID() (string, error) {
    59  	uuid, err := utils.NewUUID()
    60  	if err != nil {
    61  		return "", errors.Annotate(err, "could not create new payload ID")
    62  	}
    63  	return uuid.String(), nil
    64  }
    65  
    66  // TODO(ericsnow) Return the new ID from Track()?
    67  
    68  // Track inserts the provided payload info in state. The new Juju ID
    69  // for the payload is returned.
    70  func (uw UnitPayloads) Track(pl payload.Payload) error {
    71  	logger.Tracef("tracking %#v", pl)
    72  
    73  	if err := pl.Validate(); err != nil {
    74  		return errors.NewNotValid(err, "bad payload")
    75  	}
    76  
    77  	id, err := uw.newID()
    78  	if err != nil {
    79  		return errors.Trace(err)
    80  	}
    81  
    82  	ok, err := uw.Persist.Track(id, pl)
    83  	if err != nil {
    84  		return errors.Trace(err)
    85  	}
    86  	if !ok {
    87  		return errors.NotValidf("payload %s (already in state)", id)
    88  	}
    89  
    90  	return nil
    91  }
    92  
    93  // SetStatus updates the raw status for the identified payload to the
    94  // provided value.
    95  func (uw UnitPayloads) SetStatus(id, status string) error {
    96  	logger.Tracef("setting payload status for %q to %q", id, status)
    97  
    98  	if err := payload.ValidateState(status); err != nil {
    99  		return errors.Trace(err)
   100  	}
   101  
   102  	found, err := uw.Persist.SetStatus(id, status)
   103  	if err != nil {
   104  		return errors.Trace(err)
   105  	}
   106  	if !found {
   107  		return errors.NotFoundf(id)
   108  	}
   109  	return nil
   110  }
   111  
   112  // List builds the list of payload information for the provided payload
   113  // IDs. If none are provided then the list contains the info for all
   114  // payloads associated with the unit. Missing payloads
   115  // are ignored.
   116  func (uw UnitPayloads) List(ids ...string) ([]payload.Result, error) {
   117  	logger.Tracef("listing %v", ids)
   118  	var err error
   119  	var payloads []payload.Payload
   120  	missingIDs := make(map[string]bool)
   121  	if len(ids) == 0 {
   122  		payloads, err = uw.Persist.ListAll()
   123  		if err != nil {
   124  			return nil, errors.Trace(err)
   125  		}
   126  		for _ = range payloads {
   127  			ids = append(ids, "")
   128  		}
   129  	} else {
   130  		var missing []string
   131  		payloads, missing, err = uw.Persist.List(ids...)
   132  		if err != nil {
   133  			return nil, errors.Trace(err)
   134  		}
   135  		for _, id := range missing {
   136  			missingIDs[id] = true
   137  		}
   138  	}
   139  
   140  	var results []payload.Result
   141  	i := 0
   142  	for _, id := range ids {
   143  		if missingIDs[id] {
   144  			results = append(results, payload.Result{
   145  				ID:       id,
   146  				NotFound: true,
   147  				Error:    errors.NotFoundf(id),
   148  			})
   149  			continue
   150  		}
   151  		pl := payloads[i]
   152  		i += 1
   153  
   154  		// TODO(ericsnow) Ensure that pl.Unit == uw.Unit?
   155  
   156  		result := payload.Result{
   157  			ID: id,
   158  			Payload: &payload.FullPayloadInfo{
   159  				Payload: pl,
   160  				Machine: uw.Machine,
   161  			},
   162  		}
   163  		if id == "" {
   164  			// TODO(ericsnow) Do this more efficiently.
   165  			id, err := uw.LookUp(pl.Name, pl.ID)
   166  			if err != nil {
   167  				id = ""
   168  				result.Error = errors.Trace(err)
   169  			}
   170  			result.ID = id
   171  		}
   172  		results = append(results, result)
   173  	}
   174  	return results, nil
   175  }
   176  
   177  // LookUp returns the payload ID for the given name/rawID pair.
   178  func (uw UnitPayloads) LookUp(name, rawID string) (string, error) {
   179  	logger.Tracef("looking up payload id for %s/%s", name, rawID)
   180  
   181  	id, err := uw.Persist.LookUp(name, rawID)
   182  	if err != nil {
   183  		return "", errors.Trace(err)
   184  	}
   185  	return id, nil
   186  }
   187  
   188  // Untrack removes the identified payload from state. It does not
   189  // trigger the actual destruction of the payload.
   190  func (uw UnitPayloads) Untrack(id string) error {
   191  	logger.Tracef("untracking %q", id)
   192  	// If the record wasn't found then we're already done.
   193  	_, err := uw.Persist.Untrack(id)
   194  	if err != nil {
   195  		return errors.Trace(err)
   196  	}
   197  	return nil
   198  }