github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/multiwatcher/multiwatcher.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package multiwatcher
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/json"
     9  	"fmt"
    10  	"time"
    11  
    12  	"github.com/juju/errors"
    13  	"gopkg.in/juju/charm.v6-unstable"
    14  
    15  	"github.com/juju/juju/constraints"
    16  	"github.com/juju/juju/instance"
    17  	"github.com/juju/juju/status"
    18  )
    19  
    20  // Life describes the lifecycle state of an entity ("alive", "dying"
    21  // or "dead").
    22  type Life string
    23  
    24  // EntityInfo is implemented by all entity Info types.
    25  type EntityInfo interface {
    26  	// EntityId returns an identifier that will uniquely
    27  	// identify the entity within its kind
    28  	EntityId() EntityId
    29  }
    30  
    31  // EntityId uniquely identifies an entity being tracked by the
    32  // multiwatcherStore.
    33  type EntityId struct {
    34  	Kind      string `json:"kind"`
    35  	ModelUUID string `json:"model-uuid"`
    36  	Id        string `json:"id"`
    37  }
    38  
    39  // Delta holds details of a change to the model.
    40  type Delta struct {
    41  	// If Removed is true, the entity has been removed;
    42  	// otherwise it has been created or changed.
    43  	Removed bool `json:"removed"`
    44  	// Entity holds data about the entity that has changed.
    45  	Entity EntityInfo `json:"entity"`
    46  }
    47  
    48  // MarshalJSON implements json.Marshaler.
    49  func (d *Delta) MarshalJSON() ([]byte, error) {
    50  	b, err := json.Marshal(d.Entity)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	var buf bytes.Buffer
    55  	buf.WriteByte('[')
    56  	c := "change"
    57  	if d.Removed {
    58  		c = "remove"
    59  	}
    60  	fmt.Fprintf(&buf, "%q,%q,", d.Entity.EntityId().Kind, c)
    61  	buf.Write(b)
    62  	buf.WriteByte(']')
    63  	return buf.Bytes(), nil
    64  }
    65  
    66  // UnmarshalJSON implements json.Unmarshaler.
    67  func (d *Delta) UnmarshalJSON(data []byte) error {
    68  	var elements []json.RawMessage
    69  	if err := json.Unmarshal(data, &elements); err != nil {
    70  		return err
    71  	}
    72  	if len(elements) != 3 {
    73  		return fmt.Errorf(
    74  			"Expected 3 elements in top-level of JSON but got %d",
    75  			len(elements))
    76  	}
    77  	var entityKind, operation string
    78  	if err := json.Unmarshal(elements[0], &entityKind); err != nil {
    79  		return err
    80  	}
    81  	if err := json.Unmarshal(elements[1], &operation); err != nil {
    82  		return err
    83  	}
    84  	if operation == "remove" {
    85  		d.Removed = true
    86  	} else if operation != "change" {
    87  		return fmt.Errorf("Unexpected operation %q", operation)
    88  	}
    89  	switch entityKind {
    90  	case "model":
    91  		d.Entity = new(ModelInfo)
    92  	case "machine":
    93  		d.Entity = new(MachineInfo)
    94  	case "application":
    95  		d.Entity = new(ApplicationInfo)
    96  	case "unit":
    97  		d.Entity = new(UnitInfo)
    98  	case "relation":
    99  		d.Entity = new(RelationInfo)
   100  	case "annotation":
   101  		d.Entity = new(AnnotationInfo)
   102  	case "block":
   103  		d.Entity = new(BlockInfo)
   104  	case "action":
   105  		d.Entity = new(ActionInfo)
   106  	default:
   107  		return errors.Errorf("Unexpected entity name %q", entityKind)
   108  	}
   109  	return json.Unmarshal(elements[2], &d.Entity)
   110  }
   111  
   112  // Address describes a network address.
   113  type Address struct {
   114  	Value           string `json:"value"`
   115  	Type            string `json:"type"`
   116  	Scope           string `json:"scope"`
   117  	SpaceName       string `json:"space-name,omitempty"`
   118  	SpaceProviderId string `json:"space-provider-id,omitempty"`
   119  }
   120  
   121  // MachineInfo holds the information about a machine
   122  // that is tracked by multiwatcherStore.
   123  type MachineInfo struct {
   124  	ModelUUID                string                            `json:"model-uuid"`
   125  	Id                       string                            `json:"id"`
   126  	InstanceId               string                            `json:"instance-id"`
   127  	AgentStatus              StatusInfo                        `json:"agent-status"`
   128  	InstanceStatus           StatusInfo                        `json:"instance-status"`
   129  	Life                     Life                              `json:"life"`
   130  	Series                   string                            `json:"series"`
   131  	SupportedContainers      []instance.ContainerType          `json:"supported-containers"`
   132  	SupportedContainersKnown bool                              `json:"supported-containers-known"`
   133  	HardwareCharacteristics  *instance.HardwareCharacteristics `json:"hardware-characteristics,omitempty"`
   134  	Jobs                     []MachineJob                      `json:"jobs"`
   135  	Addresses                []Address                         `json:"addresses"`
   136  	HasVote                  bool                              `json:"has-vote"`
   137  	WantsVote                bool                              `json:"wants-vote"`
   138  }
   139  
   140  // EntityId returns a unique identifier for a machine across
   141  // models.
   142  func (i *MachineInfo) EntityId() EntityId {
   143  	return EntityId{
   144  		Kind:      "machine",
   145  		ModelUUID: i.ModelUUID,
   146  		Id:        i.Id,
   147  	}
   148  }
   149  
   150  // StatusInfo holds the unit and machine status information. It is
   151  // used by ApplicationInfo and UnitInfo.
   152  type StatusInfo struct {
   153  	Err     error                  `json:"err,omitempty"`
   154  	Current status.Status          `json:"current"`
   155  	Message string                 `json:"message"`
   156  	Since   *time.Time             `json:"since,omitempty"`
   157  	Version string                 `json:"version"`
   158  	Data    map[string]interface{} `json:"data,omitempty"`
   159  }
   160  
   161  // NewStatusInfo return a new multiwatcher StatusInfo from a
   162  // status StatusInfo.
   163  func NewStatusInfo(s status.StatusInfo, err error) StatusInfo {
   164  	return StatusInfo{
   165  		Err:     err,
   166  		Current: s.Status,
   167  		Message: s.Message,
   168  		Since:   s.Since,
   169  		Data:    s.Data,
   170  	}
   171  }
   172  
   173  // ApplicationInfo holds the information about an application that is tracked
   174  // by multiwatcherStore.
   175  type ApplicationInfo struct {
   176  	ModelUUID   string                 `json:"model-uuid"`
   177  	Name        string                 `json:"name"`
   178  	Exposed     bool                   `json:"exposed"`
   179  	CharmURL    string                 `json:"charm-url"`
   180  	OwnerTag    string                 `json:"owner-tag"`
   181  	Life        Life                   `json:"life"`
   182  	MinUnits    int                    `json:"min-units"`
   183  	Constraints constraints.Value      `json:"constraints"`
   184  	Config      map[string]interface{} `json:"config,omitempty"`
   185  	Subordinate bool                   `json:"subordinate"`
   186  	Status      StatusInfo             `json:"status"`
   187  }
   188  
   189  // EntityId returns a unique identifier for an application across
   190  // models.
   191  func (i *ApplicationInfo) EntityId() EntityId {
   192  	return EntityId{
   193  		Kind:      "application",
   194  		ModelUUID: i.ModelUUID,
   195  		Id:        i.Name,
   196  	}
   197  }
   198  
   199  // Port identifies a network port number for a particular protocol.
   200  type Port struct {
   201  	Protocol string `json:"protocol"`
   202  	Number   int    `json:"number"`
   203  }
   204  
   205  // PortRange represents a single range of ports.
   206  type PortRange struct {
   207  	FromPort int    `json:"from-port"`
   208  	ToPort   int    `json:"to-port"`
   209  	Protocol string `json:"protocol"`
   210  }
   211  
   212  // UnitInfo holds the information about a unit
   213  // that is tracked by multiwatcherStore.
   214  type UnitInfo struct {
   215  	ModelUUID      string      `json:"model-uuid"`
   216  	Name           string      `json:"name"`
   217  	Application    string      `json:"application"`
   218  	Series         string      `json:"series"`
   219  	CharmURL       string      `json:"charm-url"`
   220  	PublicAddress  string      `json:"public-address"`
   221  	PrivateAddress string      `json:"private-address"`
   222  	MachineId      string      `json:"machine-id"`
   223  	Ports          []Port      `json:"ports"`
   224  	PortRanges     []PortRange `json:"port-ranges"`
   225  	Subordinate    bool        `json:"subordinate"`
   226  	// Workload and agent state are modelled separately.
   227  	WorkloadStatus StatusInfo `json:"workload-status"`
   228  	AgentStatus    StatusInfo `json:"agent-status"`
   229  }
   230  
   231  // EntityId returns a unique identifier for a unit across
   232  // models.
   233  func (i *UnitInfo) EntityId() EntityId {
   234  	return EntityId{
   235  		Kind:      "unit",
   236  		ModelUUID: i.ModelUUID,
   237  		Id:        i.Name,
   238  	}
   239  }
   240  
   241  // ActionInfo holds the information about a action that is tracked by
   242  // multiwatcherStore.
   243  type ActionInfo struct {
   244  	ModelUUID  string                 `json:"model-uuid"`
   245  	Id         string                 `json:"id"`
   246  	Receiver   string                 `json:"receiver"`
   247  	Name       string                 `json:"name"`
   248  	Parameters map[string]interface{} `json:"parameters,omitempty"`
   249  	Status     string                 `json:"status"`
   250  	Message    string                 `json:"message"`
   251  	Results    map[string]interface{} `json:"results,omitempty"`
   252  	Enqueued   time.Time              `json:"enqueued"`
   253  	Started    time.Time              `json:"started"`
   254  	Completed  time.Time              `json:"completed"`
   255  }
   256  
   257  // EntityId returns a unique identifier for an action across
   258  // models.
   259  func (i *ActionInfo) EntityId() EntityId {
   260  	return EntityId{
   261  		Kind:      "action",
   262  		ModelUUID: i.ModelUUID,
   263  		Id:        i.Id,
   264  	}
   265  }
   266  
   267  // RelationInfo holds the information about a relation that is tracked
   268  // by multiwatcherStore.
   269  type RelationInfo struct {
   270  	ModelUUID string     `json:"model-uuid"`
   271  	Key       string     `json:"key"`
   272  	Id        int        `json:"id"`
   273  	Endpoints []Endpoint `json:"endpoints"`
   274  }
   275  
   276  // CharmRelation is a mirror struct for charm.Relation.
   277  type CharmRelation struct {
   278  	Name      string `json:"name"`
   279  	Role      string `json:"role"`
   280  	Interface string `json:"interface"`
   281  	Optional  bool   `json:"optional"`
   282  	Limit     int    `json:"limit"`
   283  	Scope     string `json:"scope"`
   284  }
   285  
   286  // NewCharmRelation creates a new local CharmRelation structure from  the
   287  // charm.Relation structure. NOTE: when we update the database to not store a
   288  // charm.Relation directly in the database, this method should take the state
   289  // structure type.
   290  func NewCharmRelation(cr charm.Relation) CharmRelation {
   291  	return CharmRelation{
   292  		Name:      cr.Name,
   293  		Role:      string(cr.Role),
   294  		Interface: cr.Interface,
   295  		Optional:  cr.Optional,
   296  		Limit:     cr.Limit,
   297  		Scope:     string(cr.Scope),
   298  	}
   299  }
   300  
   301  // Endpoint holds an application-relation pair.
   302  type Endpoint struct {
   303  	ApplicationName string        `json:"application-name"`
   304  	Relation        CharmRelation `json:"relation"`
   305  }
   306  
   307  // EntityId returns a unique identifier for a relation across
   308  // models.
   309  func (i *RelationInfo) EntityId() EntityId {
   310  	return EntityId{
   311  		Kind:      "relation",
   312  		ModelUUID: i.ModelUUID,
   313  		Id:        i.Key,
   314  	}
   315  }
   316  
   317  // AnnotationInfo holds the information about an annotation that is
   318  // tracked by multiwatcherStore.
   319  type AnnotationInfo struct {
   320  	ModelUUID   string            `json:"model-uuid"`
   321  	Tag         string            `json:"tag"`
   322  	Annotations map[string]string `json:"annotations"`
   323  }
   324  
   325  // EntityId returns a unique identifier for an annotation across
   326  // models.
   327  func (i *AnnotationInfo) EntityId() EntityId {
   328  	return EntityId{
   329  		Kind:      "annotation",
   330  		ModelUUID: i.ModelUUID,
   331  		Id:        i.Tag,
   332  	}
   333  }
   334  
   335  // MachineJob values define responsibilities that machines may be
   336  // expected to fulfil.
   337  type MachineJob string
   338  
   339  const (
   340  	JobHostUnits   MachineJob = "JobHostUnits"
   341  	JobManageModel MachineJob = "JobManageModel"
   342  )
   343  
   344  // NeedsState returns true if the job requires a state connection.
   345  func (job MachineJob) NeedsState() bool {
   346  	return job == JobManageModel
   347  }
   348  
   349  // AnyJobNeedsState returns true if any of the provided jobs
   350  // require a state connection.
   351  func AnyJobNeedsState(jobs ...MachineJob) bool {
   352  	for _, j := range jobs {
   353  		if j.NeedsState() {
   354  			return true
   355  		}
   356  	}
   357  	return false
   358  }
   359  
   360  // BlockInfo holds the information about a block that is tracked by
   361  // multiwatcherStore.
   362  type BlockInfo struct {
   363  	ModelUUID string    `json:"model-uuid"`
   364  	Id        string    `json:"id"`
   365  	Type      BlockType `json:"type"`
   366  	Message   string    `json:"message"`
   367  	Tag       string    `json:"tag"`
   368  }
   369  
   370  // EntityId returns a unique identifier for a block across
   371  // models.
   372  func (i *BlockInfo) EntityId() EntityId {
   373  	return EntityId{
   374  		Kind:      "block",
   375  		ModelUUID: i.ModelUUID,
   376  		Id:        i.Id,
   377  	}
   378  }
   379  
   380  // BlockType values define model block type.
   381  type BlockType string
   382  
   383  const (
   384  	// BlockDestroy type identifies destroy blocks.
   385  	BlockDestroy BlockType = "BlockDestroy"
   386  
   387  	// BlockRemove type identifies remove blocks.
   388  	BlockRemove BlockType = "BlockRemove"
   389  
   390  	// BlockChange type identifies change blocks.
   391  	BlockChange BlockType = "BlockChange"
   392  )
   393  
   394  // ModelInfo holds the information about an model that is
   395  // tracked by multiwatcherStore.
   396  type ModelInfo struct {
   397  	ModelUUID      string `json:"model-uuid"`
   398  	Name           string `json:"name"`
   399  	Life           Life   `json:"life"`
   400  	Owner          string `json:"owner"`
   401  	ControllerUUID string `json:"controller-uuid"`
   402  }
   403  
   404  // EntityId returns a unique identifier for an model.
   405  func (i *ModelInfo) EntityId() EntityId {
   406  	return EntityId{
   407  		Kind:      "model",
   408  		ModelUUID: i.ModelUUID,
   409  		Id:        i.ModelUUID,
   410  	}
   411  }