func TestCounter(t *testing.T) { db := modeltesthelper.NewMongoDB(t) defer db.Close() if err := delCounters(); err != nil { t.Fatalf("delCounters()=%s", err) } counters := []*models.Counter{{ ID: bson.NewObjectId(), Namespace: "foo", Type: "member_instaces", Current: 10, }, { ID: bson.NewObjectId(), Namespace: "foo", Type: "member_stacks", Current: 1, }, { ID: bson.NewObjectId(), Namespace: "bar", Type: "member_instances", Current: 2, }, { ID: bson.NewObjectId(), Namespace: "bar", Type: "member_stacks", Current: 5, }} sort.Sort(models.Counters(counters)) if err := modelhelper.CreateCounters(counters...); err != nil { t.Fatalf("CreateCounters()=%s", err) } c, err := allCounters() if err != nil { t.Fatalf("counters()=%s", err) } if !reflect.DeepEqual(counters, c) { t.Fatalf("got %+v, want %+v", c, counters) } decCases := []struct { n int want int }{ {7, 3}, // i=0 {1, 0}, // i=1 {2, 0}, // i=2 {3, 2}, // i=3 } for i, cas := range decCases { c := counters[i] err := modelhelper.DecrementOrCreateCounter(c.Namespace, c.Type, cas.n) if err != nil { t.Errorf("%d (%s): DecrementOrCreateCounter()=%s", i, c.ID.Hex(), err) continue } got, err := modelhelper.CounterByID(c.ID) if err != nil { t.Errorf("%d (%s): CounterByID()=%d", i, c.ID.Hex(), err) continue } if got.Current != cas.want { t.Errorf("%d (%s): got %d, want %d", i, c.ID.Hex(), got.Current, cas.want) } } newCases := []struct { namespace string typ string n int }{ {"qux", "member_stacks", 1}, // i=0 {"qux", "member_instances", 3}, // i=0 } for i, cas := range newCases { err := modelhelper.DecrementOrCreateCounter(cas.namespace, cas.typ, cas.n) if err != nil { t.Errorf("%d: DecrementOrCreateCounter()=%s", i, err) } } c, err = modelhelper.CountersByNamespace("qux") if err != nil { t.Fatalf("CountersByNamespace()=%s", err) } if len(c) != 2 { t.Fatalf("got len(c)=%d, want len(c)=2", len(c)) } }
// Detach implements the Database interface. func (db *mongoDatabase) Detach(opts *DetachOptions) error { const detachReason = "Stack destroy requested." if err := opts.Valid(); err != nil { return err } // 1) Detach stack from user. Failure is critical, as upon return // a user would not be able to create new stack. detachStack := modelhelper.Selector{ "targetName": "JStackTemplate", "targetId": opts.Stack.BaseStackId, "sourceName": "JAccount", "sourceId": opts.Stack.OriginId, "as": "user", } err := modelhelper.DeleteRelationships(detachStack) if err != nil && err != mgo.ErrNotFound { return err } // 2) Set stack to "destroying" state. id := opts.Stack.Id.Hex() err = modelhelper.SetStackState(id, detachReason, stackstate.Destroying) if err != nil && err != mgo.ErrNotFound { // Stack state update failure is not critical, as jComputeStack // is going to be removed either way at the end of destroy op. opts.Log.Error("unable to set stack state to %q", stackstate.Destroying) } // 3) Update counters. err = modelhelper.DecrementOrCreateCounter(opts.Stack.Group, modelhelper.CounterStacks, 1) if err != nil { // Counter update is not crucial, nevertheless we log an error // if updating failed for whatever reason. opts.Log.Error("failure updating %q counter", modelhelper.CounterStacks) } err = modelhelper.DecrementOrCreateCounter(opts.Stack.Group, modelhelper.CounterInstances, len(opts.Stack.Machines)) if err != nil { // Counter update is not crucial, nevertheless we log an error // if updating failed for whatever reason. opts.Log.Error("failure updating %q counter", modelhelper.CounterInstances) } // 4) Detach machines from user. detachMachines := bson.M{ "$set": bson.M{ "status.state": "Terminated", "users": []interface{}{}, }, } err = modelhelper.UpdateMachines(detachMachines, opts.Stack.Machines...) if err != nil && err != mgo.ErrNotFound { // Detaching users from machines error is not critical, as the jMachine // documents are going to be deleted at the end of destroy operation. // Nevertheless we log error in case a troubleshooting would be needed. opts.Log.Error("detaching users from machines failed: %s", err) } return nil }