github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/constraints.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/mgo/v3"
    11  	"github.com/juju/mgo/v3/bson"
    12  	"github.com/juju/mgo/v3/txn"
    13  
    14  	"github.com/juju/juju/core/constraints"
    15  	"github.com/juju/juju/core/instance"
    16  )
    17  
    18  // constraintsDoc is the Mongo DB representation of a constraints.Value.
    19  type constraintsDoc struct {
    20  	DocID            string `bson:"_id,omitempty"`
    21  	ModelUUID        string `bson:"model-uuid"`
    22  	Arch             *string
    23  	CpuCores         *uint64
    24  	CpuPower         *uint64
    25  	Mem              *uint64
    26  	RootDisk         *uint64
    27  	RootDiskSource   *string
    28  	InstanceRole     *string
    29  	InstanceType     *string
    30  	Container        *instance.ContainerType
    31  	Tags             *[]string
    32  	Spaces           *[]string
    33  	VirtType         *string
    34  	Zones            *[]string
    35  	AllocatePublicIP *bool
    36  	ImageID          *string
    37  }
    38  
    39  func newConstraintsDoc(cons constraints.Value, id string) constraintsDoc {
    40  	result := constraintsDoc{
    41  		DocID:            id,
    42  		Arch:             cons.Arch,
    43  		CpuCores:         cons.CpuCores,
    44  		CpuPower:         cons.CpuPower,
    45  		Mem:              cons.Mem,
    46  		RootDisk:         cons.RootDisk,
    47  		RootDiskSource:   cons.RootDiskSource,
    48  		InstanceRole:     cons.InstanceRole,
    49  		InstanceType:     cons.InstanceType,
    50  		Container:        cons.Container,
    51  		Tags:             cons.Tags,
    52  		Spaces:           cons.Spaces,
    53  		VirtType:         cons.VirtType,
    54  		Zones:            cons.Zones,
    55  		AllocatePublicIP: cons.AllocatePublicIP,
    56  		ImageID:          cons.ImageID,
    57  	}
    58  	return result
    59  }
    60  
    61  func (doc constraintsDoc) value() constraints.Value {
    62  	result := constraints.Value{
    63  		Arch:             doc.Arch,
    64  		CpuCores:         doc.CpuCores,
    65  		CpuPower:         doc.CpuPower,
    66  		Mem:              doc.Mem,
    67  		RootDisk:         doc.RootDisk,
    68  		RootDiskSource:   doc.RootDiskSource,
    69  		InstanceRole:     doc.InstanceRole,
    70  		InstanceType:     doc.InstanceType,
    71  		Container:        doc.Container,
    72  		Tags:             doc.Tags,
    73  		Spaces:           doc.Spaces,
    74  		VirtType:         doc.VirtType,
    75  		Zones:            doc.Zones,
    76  		AllocatePublicIP: doc.AllocatePublicIP,
    77  		ImageID:          doc.ImageID,
    78  	}
    79  	return result
    80  }
    81  
    82  // Constraints represents the state of a constraints with an ID.
    83  type Constraints struct {
    84  	doc constraintsDoc
    85  }
    86  
    87  // ID returns the Mongo document ID for the constraints instance.
    88  func (c *Constraints) ID() string {
    89  	return c.doc.DocID
    90  }
    91  
    92  // Value returns the constraints.Value represented by the Constraints'
    93  // Mongo document.
    94  func (c *Constraints) Value() constraints.Value {
    95  	return c.doc.value()
    96  }
    97  
    98  // ChangeSpaceNameOps returns the transaction operations required to rename
    99  // a space used in these constraints.
   100  func (c *Constraints) ChangeSpaceNameOps(from, to string) []txn.Op {
   101  	val := c.doc.value()
   102  	spaces := val.Spaces
   103  	if spaces == nil {
   104  		return nil
   105  	}
   106  
   107  	for i, space := range *spaces {
   108  		if space == from {
   109  			(*spaces)[i] = to
   110  			break
   111  		}
   112  		if space == "^"+from {
   113  			(*spaces)[i] = "^" + to
   114  		}
   115  	}
   116  
   117  	return []txn.Op{setConstraintsOp(c.ID(), val)}
   118  }
   119  
   120  // AllConstraints returns all constraints in the collection.
   121  func (st *State) AllConstraints() ([]*Constraints, error) {
   122  	constraintsCollection, closer := st.db().GetCollection(constraintsC)
   123  	defer closer()
   124  	var docs []constraintsDoc
   125  	err := constraintsCollection.Find(nil).All(&docs)
   126  
   127  	cons := make([]*Constraints, len(docs))
   128  	for i, doc := range docs {
   129  		cons[i] = &Constraints{doc: doc}
   130  	}
   131  	return cons, err
   132  }
   133  
   134  // ConstraintsBySpaceName returns all Constraints that include a positive
   135  // or negative space constraint for the input space name.
   136  func (st *State) ConstraintsBySpaceName(spaceName string) ([]*Constraints, error) {
   137  	constraintsCollection, closer := st.db().GetCollection(constraintsC)
   138  	defer closer()
   139  
   140  	var docs []constraintsDoc
   141  	query := bson.D{{"$or", []bson.D{
   142  		{{"spaces", spaceName}},
   143  		{{"spaces", "^" + spaceName}},
   144  	}}}
   145  	err := constraintsCollection.Find(query).All(&docs)
   146  
   147  	cons := make([]*Constraints, len(docs))
   148  	for i, doc := range docs {
   149  		cons[i] = &Constraints{doc: doc}
   150  	}
   151  	return cons, err
   152  }
   153  
   154  func createConstraintsOp(id string, cons constraints.Value) txn.Op {
   155  	return txn.Op{
   156  		C:      constraintsC,
   157  		Id:     id,
   158  		Assert: txn.DocMissing,
   159  		Insert: newConstraintsDoc(cons, id),
   160  	}
   161  }
   162  
   163  func setConstraintsOp(id string, cons constraints.Value) txn.Op {
   164  	return txn.Op{
   165  		C:      constraintsC,
   166  		Id:     id,
   167  		Assert: txn.DocExists,
   168  		Update: bson.D{{"$set", newConstraintsDoc(cons, id)}},
   169  	}
   170  }
   171  
   172  func removeConstraintsOp(id string) txn.Op {
   173  	return txn.Op{
   174  		C:      constraintsC,
   175  		Id:     id,
   176  		Remove: true,
   177  	}
   178  }
   179  
   180  func readConstraints(mb modelBackend, id string) (constraints.Value, error) {
   181  	constraintsCollection, closer := mb.db().GetCollection(constraintsC)
   182  	defer closer()
   183  
   184  	var doc constraintsDoc
   185  	if err := constraintsCollection.FindId(id).One(&doc); err == mgo.ErrNotFound {
   186  		return constraints.Value{}, errors.NotFoundf("constraints")
   187  	} else if err != nil {
   188  		return constraints.Value{}, err
   189  	}
   190  	return doc.value(), nil
   191  }
   192  
   193  func writeConstraints(mb modelBackend, id string, cons constraints.Value) error {
   194  	ops := []txn.Op{setConstraintsOp(id, cons)}
   195  	if err := mb.db().RunTransaction(ops); err != nil {
   196  		return fmt.Errorf("cannot set constraints: %v", err)
   197  	}
   198  	return nil
   199  }
   200  
   201  // AllConstraints retrieves all the constraints in the model
   202  // and provides a way to query based on machine or application.
   203  func (m *Model) AllConstraints() (*ModelConstraints, error) {
   204  	coll, closer := m.st.db().GetCollection(constraintsC)
   205  	defer closer()
   206  
   207  	var docs []constraintsDoc
   208  	err := coll.Find(nil).All(&docs)
   209  	if err != nil {
   210  		return nil, errors.Annotate(err, "cannot get all constraints for model")
   211  	}
   212  	all := &ModelConstraints{
   213  		data: make(map[string]constraintsDoc),
   214  	}
   215  	for _, doc := range docs {
   216  		all.data[m.localID(doc.DocID)] = doc
   217  	}
   218  	return all, nil
   219  }
   220  
   221  // ModelConstraints represents all the constraints in  a model
   222  // keyed on global key.
   223  type ModelConstraints struct {
   224  	data map[string]constraintsDoc
   225  }
   226  
   227  // Machine returns the constraints for the specified machine.
   228  func (c *ModelConstraints) Machine(machineID string) constraints.Value {
   229  	doc, found := c.data[machineGlobalKey(machineID)]
   230  	if !found {
   231  		return constraints.Value{}
   232  	}
   233  	return doc.value()
   234  }
   235  
   236  // TODO: add methods for Model and Application constraints checks
   237  // if desired. Not currently used in status calls.