809
811
return u.assignToMachine(m, false)
812
// AssignToNewMachine assigns the unit to a new machine, with constraints
813
// determined according to the service and environment constraints at the
814
// time of unit creation.
815
func (u *Unit) AssignToNewMachine() (err error) {
816
defer assignContextf(&err, u, "new machine")
817
if u.doc.Principal != "" {
818
return fmt.Errorf("unit is a subordinate")
820
// Get the ops necessary to create a new machine, and the machine doc that
821
// will be added with those operations (which includes the machine id).
822
cons, err := readConstraints(u.st, u.globalKey())
823
if errors.IsNotFoundError(err) {
824
// Lack of constraints indicates lack of unit.
825
return errors.NotFoundf("unit")
826
} else if err != nil {
829
var containerType instance.ContainerType
830
// Configure to create a new container if required.
831
if cons.Container != nil && *cons.Container != "" && *cons.Container != instance.NONE {
832
containerType = *cons.Container
834
params := &AddMachineParams{
835
Series: u.doc.Series,
836
ContainerType: containerType,
837
Jobs: []MachineJob{JobHostUnits},
814
// assignToNewMachine assigns the unit to a machine created according to the supplied params,
815
// with the supplied constraints.
816
func (u *Unit) assignToNewMachine(params *AddMachineParams, cons constraints.Value) (err error) {
839
817
ops, instData, containerParams, err := u.st.addMachineContainerOps(params, cons)
843
821
mdoc := &machineDoc{
844
822
Series: u.doc.Series,
845
ContainerType: string(containerType),
823
ContainerType: string(params.ContainerType),
846
824
Jobs: []MachineJob{JobHostUnits},
847
825
Principals: []string{u.doc.Name},
854
832
ops = append(ops, machineOps...)
855
833
isUnassigned := D{{"machineid", ""}}
834
asserts := append(isAliveDoc, isUnassigned...)
835
// Ensure the host machine is really clean.
836
if params.ParentId != "" {
837
ops = append(ops, txn.Op{
838
C: u.st.machines.Name,
840
Assert: D{{"clean", true}},
842
C: u.st.containerRefs.Name,
844
Assert: D{hasNoContainersTerm},
856
847
ops = append(ops, txn.Op{
857
848
C: u.st.units.Name,
859
Assert: append(isAliveDoc, isUnassigned...),
860
851
Update: D{{"$set", D{{"machineid", mdoc.Id}}}},
862
853
err = u.st.runTransaction(ops)
881
873
case unit.doc.MachineId != "":
882
874
return alreadyAssignedErr
876
if params.ParentId != "" {
877
m, err := u.st.Machine(params.ParentId)
882
return machineNotCleanErr
884
containers, err := m.Containers()
888
if len(containers) > 0 {
889
return machineNotCleanErr
884
892
// Other error condition not considered.
885
893
return fmt.Errorf("unknown error")
896
// constraints is a helper function to return a unit's deployment constraints.
897
func (u *Unit) constraints() (*constraints.Value, error) {
898
cons, err := readConstraints(u.st, u.globalKey())
899
if errors.IsNotFoundError(err) {
900
// Lack of constraints indicates lack of unit.
901
return nil, errors.NotFoundf("unit")
902
} else if err != nil {
908
// AssignToNewMachineOrContainer assigns the unit to a new machine, with constraints
909
// determined according to the service and environment constraints at the time of unit creation.
910
// If a container is required, a clean, empty machine instance is required on which to create
911
// the container. An existing clean, empty instance is first searched for, and if not found,
912
// a new one is created.
913
func (u *Unit) AssignToNewMachineOrContainer() (err error) {
914
defer assignContextf(&err, u, "new machine or container")
915
if u.doc.Principal != "" {
916
return fmt.Errorf("unit is a subordinate")
918
cons, err := u.constraints()
922
if !cons.HasContainer() {
923
return u.AssignToNewMachine()
926
// Find a clean, empty machine on which to create a container.
929
noContainer := instance.NONE
930
hostCons.Container = &noContainer
931
query, err := u.findCleanMachineQuery(true, &hostCons)
935
err = query.One(&host)
936
if err == mgo.ErrNotFound {
937
// No existing clean, empty machine so create a new one.
938
// The container constraint will be used by AssignToNewMachine to create the required container.
939
return u.AssignToNewMachine()
940
} else if err != nil {
943
params := &AddMachineParams{
944
Series: u.doc.Series,
946
ContainerType: *cons.Container,
947
Jobs: []MachineJob{JobHostUnits},
949
err = u.assignToNewMachine(params, *cons)
950
if err == machineNotCleanErr {
951
// The clean machine was used before we got a chance to use it so just
952
// stick the unit on a new machine.
953
return u.AssignToNewMachine()
958
// AssignToNewMachine assigns the unit to a new machine, with constraints
959
// determined according to the service and environment constraints at the
960
// time of unit creation.
961
func (u *Unit) AssignToNewMachine() (err error) {
962
defer assignContextf(&err, u, "new machine")
963
if u.doc.Principal != "" {
964
return fmt.Errorf("unit is a subordinate")
966
// Get the ops necessary to create a new machine, and the machine doc that
967
// will be added with those operations (which includes the machine id).
968
cons, err := u.constraints()
972
var containerType instance.ContainerType
973
// Configure to create a new container if required.
974
if cons.HasContainer() {
975
containerType = *cons.Container
977
params := &AddMachineParams{
978
Series: u.doc.Series,
979
ContainerType: containerType,
980
Jobs: []MachineJob{JobHostUnits},
982
err = u.assignToNewMachine(params, *cons)
888
986
var noCleanMachines = stderrors.New("all eligible machines in use")
890
988
// AssignToCleanMachine assigns u to a machine which is marked as clean. A machine
907
1005
return u.assignToCleanMaybeEmptyMachine(true)
910
// assignToCleanMaybeEmptyMachine implements AssignToCleanMachine and AssignToCleanEmptyMachine.
911
func (u *Unit) assignToCleanMaybeEmptyMachine(requireEmpty bool) (m *Machine, err error) {
1008
var hasContainerTerm = bson.DocElem{
1010
{{"children", D{{"$not", D{{"$size", 0}}}}}},
1011
{{"children", D{{"$exists", true}}}},
1014
var hasNoContainersTerm = bson.DocElem{
1016
{{"children", D{{"$size", 0}}}},
1017
{{"children", D{{"$exists", false}}}},
1020
// findCleanMachineQuery returns a Mongo query to find clean (and possibly empty) machines with
1021
// characteristics matching the specified constraints.
1022
func (u *Unit) findCleanMachineQuery(requireEmpty bool, cons *constraints.Value) (*mgo.Query, error) {
1023
// TODO(wallyworld) - add support for constraints besides just container type
1024
var containerType instance.ContainerType
1025
if cons.Container != nil {
1026
containerType = *cons.Container
912
1028
// Select all machines that can accept principal units and are clean.
913
1029
var containerRefs []machineContainers
914
1030
// If we need empty machines, first build up a list of machine ids which have containers
915
1031
// so we can exclude those.
916
1032
if requireEmpty {
917
err = u.st.containerRefs.Find(D{
921
D{{"$not", D{{"$size", 0}}}},
925
D{{"$exists", true}},
929
).Select(bson.M{"_id": 1}).All(&containerRefs)
1033
err := u.st.containerRefs.Find(D{hasContainerTerm}).Select(bson.M{"_id": 1}).All(&containerRefs)
935
1039
for i, cref := range containerRefs {
936
1040
machinesWithContainers[i] = cref.Id
938
query := u.st.machines.Find(D{
939
1043
{"life", Alive},
940
1044
{"series", u.doc.Series},
941
{"jobs", JobHostUnits},
942
{"clean", D{{"$ne", false}}},
1045
{"jobs", []MachineJob{JobHostUnits}},
943
1047
{"_id", D{{"$nin", machinesWithContainers}}},
1049
// Add the container filter term if necessary.
1050
if containerType == instance.NONE {
1051
terms = append(terms, bson.DocElem{"containertype", ""})
1052
} else if containerType != "" {
1053
terms = append(terms, bson.DocElem{"containertype", string(containerType)})
1055
return u.st.machines.Find(terms), nil
1058
// assignToCleanMaybeEmptyMachine implements AssignToCleanMachine and AssignToCleanEmptyMachine.
1059
// A 'machine' may be a machine instance or container depending on the service constraints.
1060
func (u *Unit) assignToCleanMaybeEmptyMachine(requireEmpty bool) (m *Machine, err error) {
1063
context += ", empty"
1065
context += " machine"
1067
if u.doc.Principal != "" {
1068
err = fmt.Errorf("unit is a subordinate")
1069
assignContextf(&err, u, context)
1073
// Get the unit constraints to see what deployment requirements we have to adhere to.
1074
cons, err := u.constraints()
1076
assignContextf(&err, u, context)
1079
query, err := u.findCleanMachineQuery(requireEmpty, cons)
1081
assignContextf(&err, u, context)
946
1085
// TODO use Batch(1). See https://bugs.launchpad.net/mgo/+bug/1053509
947
1086
// TODO(rog) Fix so this is more efficient when there are concurrent uses.