github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/state/multiwatcher/multiwatcher.go (about)

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