Exemple #1
0
func (q *Queue) CheckUsage(providerName string, m provider.Machine, bm *provider.BaseMachine, ctx context.Context) error {
	q.Log.Debug("Checking %q machine\n%+v\n", providerName, bm.Machine)

	c, err := klient.Connect(q.Kite, bm.QueryString)
	if err != nil {
		q.Log.Debug("Error connecting to klient, stopping if needed. Error: %s", err)
		return err
	}

	// replace with the real and authenticated username
	if bm.User == nil {
		bm.User = &models.User{}
	}

	bm.User.Name = c.Username

	// get the usage directly from the klient, which is the most predictable source
	usg, err := c.Usage()
	c.Close() // close the underlying connection once we get the usage
	if err != nil {
		return fmt.Errorf("failure getting %q klient usage: %s", bm.QueryString, err)
	}

	q.Log.Debug("machine [%s] (aws) is inactive for %s (plan limit: %s)",
		bm.IpAddress, usg.InactiveDuration, planTimeout)

	// It still have plenty of time to work, do not stop it
	if usg.InactiveDuration <= planTimeout {
		return nil
	}

	q.Log.Info("machine [%s] has reached current plan limit of %s. Shutting down...",
		bm.IpAddress, usg.InactiveDuration)

	// Hasta la vista, baby!
	q.Log.Info("[%s] ======> STOP started (closing inactive machine)<======", bm.ObjectId.Hex())

	meta, err := m.Stop(ctx)
	if err != nil {
		// returning is ok, because Kloud will mark it anyways as stopped if
		// Klient is not rechable anymore with the `info` method
		q.Log.Info("[%s] ======> STOP aborted (closing inactive machine: %s)<======", bm.ObjectId.Hex(), err)

		return err
	}

	q.Log.Info("[%s] ======> STOP finished (closing inactive machine)<======", bm.ObjectId.Hex())

	obj := object.MetaBuilder.Build(meta)
	obj["status.modifiedAt"] = time.Now().UTC()
	obj["status.state"] = machinestate.Stopped.String()
	obj["status.reason"] = "Machine is stopped due to inactivity"

	return modelhelper.UpdateMachine(bm.ObjectId, bson.M{"$set": obj})
}
Exemple #2
0
// Migrate implements the Database interface.
func (db *mongoDatabase) Migrate(opts *MigrateOptions) error {
	stack := models.NewStackTemplate(opts.Provider, opts.Identifier)
	stack.Machines = make([]bson.M, len(opts.Machines))

	for i := range stack.Machines {
		stack.Machines[i] = bson.M(machineBuilder.Build(opts.Machines[i]))
	}

	account, err := modelhelper.GetAccount(opts.Username)
	if err != nil {
		return fmt.Errorf("account lookup failed for %q: %s", opts.Username, err)
	}

	sum := sha1.Sum([]byte(opts.Template))

	stack.Title = opts.StackName
	stack.OriginID = account.Id
	stack.Template.Details = bson.M{
		"lastUpdaterId": account.Id,
	}
	stack.Group = opts.GroupName
	stack.Template.Content = opts.Template
	stack.Template.Sum = hex.EncodeToString(sum[:])

	if s, err := yamlReencode(opts.Template); err == nil {
		stack.Template.RawContent = s
	}

	if err := modelhelper.CreateStackTemplate(stack); err != nil {
		return fmt.Errorf("failed to create stack template: %s", err)
	}

	change := bson.M{
		"$set": bson.M{
			"meta.migration.modifiedAt":      time.Now(),
			"meta.migration.status":          MigrationMigrated,
			"meta.migration.stackTemplateId": stack.Id,
		},
	}

	for _, id := range opts.MachineIDs {
		if e := modelhelper.UpdateMachine(id, change); e != nil {
			err = multierror.Append(err, fmt.Errorf("failed to update migration details for %q: %s", id.Hex(), err))
		}
	}

	// Failure updating jMachine migration metadata is not critical,
	// just log the error and continue.
	if err != nil {
		opts.Log.Error("%s", err)
	}

	return nil
}
Exemple #3
0
func (cmd *GroupFixDomain) update(records []*dnsclient.Record, ids []bson.ObjectId) error {
	merr := new(multierror.Error)

	for i, rec := range records {
		err := modelhelper.UpdateMachine(ids[i], bson.M{"domain": rec.Name})
		if err != nil {
			err = fmt.Errorf("failed updating %q domain to %q: %s", ids[i].Hex(), rec.Name, err)
			merr = multierror.Append(merr, err)
		}
	}

	return merr.ErrorOrNil()
}
Exemple #4
0
func (bs *BaseStack) UpdateResources(state *terraform.State) error {
	machines, err := bs.state(state, bs.Klients)
	if err != nil {
		return err
	}

	now := time.Now().UTC()

	for label, m := range bs.Builder.Machines {
		machine, ok := machines[label]
		if !ok {
			err = multierror.Append(err, fmt.Errorf("machine %q does not exist in terraform state file", label))
			continue
		}

		if machine.Provider != bs.Planner.Provider {
			continue
		}

		if cred, err := bs.Builder.CredentialByProvider(machine.Provider); err == nil {
			machine.Credential = cred
		} else {
			err = multierror.Append(err, fmt.Errorf("machine %q: no credential found for %q provider", label, machine.Provider))
			machine.Credential = &stack.Credential{}
		}

		state, ok := bs.Klients[label]
		if !ok {
			err = multierror.Append(err, fmt.Errorf("machine %q does not exist in dial state", label))
			continue
		}

		err := modelhelper.UpdateMachine(m.ObjectId, bson.M{"$set": bs.buildUpdateObj(machine, state, now)})
		if err != nil {
			err = multierror.Append(err, fmt.Errorf("machine %q failed to update: %s", label, err))
			continue
		}
	}

	return err
}
Exemple #5
0
func (bm *BaseMachine) updateMachine(state *DialState, meta interface{}, dbState machinestate.State) error {
	obj := object.MetaBuilder.Build(meta)

	if state != nil && state.KiteURL != "" {
		if bm.RegisterURL != state.KiteURL {
			obj["registerUrl"] = state.KiteURL
		}

		if u, err := url.Parse(state.KiteURL); err == nil && u.Host != "" {
			if host, _, err := net.SplitHostPort(u.Host); err == nil {
				u.Host = host
			}

			if bm.IpAddress != u.Host {
				// TODO(rjeczalik): when path routing is added (#9021) either we
				// change the ipAddress field to more generic endpoint field,
				// or we use here state.KiteURL directly.
				obj["ipAddress"] = u.Host
			}
		}
	}

	if dbState != 0 {
		obj["status.modifiedAt"] = time.Now().UTC()
		obj["status.state"] = dbState.String()
		obj["status.reason"] = "Machine is " + dbState.String()
	}

	if len(obj) == 0 {
		return nil
	}

	bm.Log.Debug("update object for %q: %+v (%# v)", bm.Label, obj, state)

	return modelhelper.UpdateMachine(bm.ObjectId, bson.M{"$set": obj})
}
Exemple #6
0
func TestLookupGroup(t *testing.T) {
	const N = 10

	db := modeltesthelper.NewMongoDB(t)
	defer db.Close()

	user := &models.User{
		ObjectId: bson.NewObjectId(),
		Name:     bson.NewObjectId().Hex(),
		Email:    bson.NewObjectId().Hex(),
	}

	if err := modelhelper.CreateUser(user); err != nil {
		t.Fatalf("CreateUser()=%s", err)
	}

	groups, err := createGroups(N + 1)
	if err != nil {
		t.Fatalf("createGroups()=%s", err)
	}

	machines, err := createMachines(N, t)
	if err != nil {
		t.Fatalf("createMachines()=%s", err)
	}

	for i := range machines {
		machines[i].Groups = []models.MachineGroup{{Id: groups[i].Id}}

		update := bson.M{
			"$set": bson.M{
				"groups": machines[i].Groups,
				"users": []*models.MachineUser{{
					Id:       user.ObjectId,
					Username: user.Name,
				}},
			},
		}

		if i&2 == 0 {
			// force to lookup by registerUrl for machines with even index
			update["$set"].(bson.M)["ipAddress"] = ""
		}

		err := modelhelper.UpdateMachine(machines[i].ObjectId, update)
		if err != nil {
			t.Fatalf("UpdateMachine()=%s", err)
		}
	}

	session := &models.Session{
		Id:        bson.NewObjectId(),
		GroupName: groups[N].Slug,
		Username:  user.Name,
	}

	if err := modelhelper.CreateSession(session); err != nil {
		t.Fatalf("CreateSession()=%s")
	}

	cases := map[string]struct {
		opts *modelhelper.LookupGroupOptions
		id   bson.ObjectId
	}{
		"lookup by queryString": {
			&modelhelper.LookupGroupOptions{
				Username: user.Name,
				KiteID:   mustKiteID(machines[0].QueryString),
			},
			groups[0].Id,
		},
		"lookup by ipAddress": {
			&modelhelper.LookupGroupOptions{
				Username:  user.Name,
				ClientURL: machines[1].RegisterURL,
			},
			groups[1].Id,
		},
		"lookup by registerUrl": {
			&modelhelper.LookupGroupOptions{
				Username:  user.Name,
				ClientURL: machines[4].RegisterURL,
			},
			groups[4].Id,
		},
		"lookup by most recent session for KD": {
			&modelhelper.LookupGroupOptions{
				Username:    user.Name,
				Environment: "managed",
			},
			groups[N].Id,
		},
	}

	for name, cas := range cases {
		t.Run(name, func(t *testing.T) {
			team, err := modelhelper.LookupGroup(cas.opts)
			if err != nil {
				t.Fatalf("LookupGroup()=%s", err)
			}

			if team.Id != cas.id {
				t.Fatalf("got %q, want %q", team.Id.Hex(), cas.id.Hex())
			}
		})
	}
}
Exemple #7
0
// UpdateMigration implements the Database interface.
func (db *mongoDatabase) UpdateMigration(opts *UpdateMigrationOptions) error {
	change := bson.M{
		"$set": migrationBuilder.Build(opts.Meta),
	}
	return modelhelper.UpdateMachine(opts.MachineID, change)
}