Beispiel #1
0
// apiInfoConnect looks for endpoint on the given environment and
// tries to connect to it, sending the result on the returned channel.
func apiInfoConnect(info configstore.EnvironInfo, apiOpen api.OpenFunc, stop <-chan struct{}, bClient *httpbakery.Client) (api.Connection, error) {
	endpoint := info.APIEndpoint()
	if info == nil || len(endpoint.Addresses) == 0 {
		return nil, &infoConnectError{fmt.Errorf("no cached addresses")}
	}
	logger.Infof("connecting to API addresses: %v", endpoint.Addresses)
	var modelTag names.ModelTag
	if names.IsValidModel(endpoint.ModelUUID) {
		modelTag = names.NewModelTag(endpoint.ModelUUID)
	}

	apiInfo := &api.Info{
		Addrs:    endpoint.Addresses,
		CACert:   endpoint.CACert,
		Tag:      environInfoUserTag(info),
		Password: info.APICredentials().Password,
		ModelTag: modelTag,
	}
	if apiInfo.Tag == nil {
		apiInfo.UseMacaroons = true
	}

	dialOpts := api.DefaultDialOpts()
	dialOpts.BakeryClient = bClient

	st, err := apiOpen(apiInfo, dialOpts)
	if err != nil {
		return nil, &infoConnectError{err}
	}
	return st, nil
}
Beispiel #2
0
// Validate returns an error if the TargetInfo contains bad data. Nil
// is returned otherwise.
func (info *TargetInfo) Validate() error {
	if !names.IsValidModel(info.ControllerTag.Id()) {
		return errors.NotValidf("ControllerTag")
	}

	if len(info.Addrs) < 1 {
		return errors.NotValidf("empty Addrs")
	}
	for _, addr := range info.Addrs {
		_, err := network.ParseHostPort(addr)
		if err != nil {
			return errors.NotValidf("%q in Addrs", addr)
		}
	}

	if info.CACert == "" {
		return errors.NotValidf("empty CACert")
	}

	if info.AuthTag.Id() == "" {
		return errors.NotValidf("empty AuthTag")
	}

	if info.Password == "" {
		return errors.NotValidf("empty Password")
	}

	return nil
}
Beispiel #3
0
// NewAgentConfig returns a new config object suitable for use for a
// machine or unit agent.
func NewAgentConfig(configParams AgentConfigParams) (ConfigSetterWriter, error) {
	if configParams.Paths.DataDir == "" {
		return nil, errors.Trace(requiredError("data directory"))
	}
	if configParams.Tag == nil {
		return nil, errors.Trace(requiredError("entity tag"))
	}
	switch configParams.Tag.(type) {
	case names.MachineTag, names.UnitTag:
		// these are the only two type of tags that can represent an agent
	default:
		return nil, errors.Errorf("entity tag must be MachineTag or UnitTag, got %T", configParams.Tag)
	}
	if configParams.UpgradedToVersion == version.Zero {
		return nil, errors.Trace(requiredError("upgradedToVersion"))
	}
	if configParams.Password == "" {
		return nil, errors.Trace(requiredError("password"))
	}
	if uuid := configParams.Model.Id(); uuid == "" {
		return nil, errors.Trace(requiredError("model"))
	} else if !names.IsValidModel(uuid) {
		return nil, errors.Errorf("%q is not a valid model uuid", uuid)
	}
	if len(configParams.CACert) == 0 {
		return nil, errors.Trace(requiredError("CA certificate"))
	}
	// Note that the password parts of the state and api information are
	// blank.  This is by design.
	config := &configInternal{
		paths:             NewPathsWithDefaults(configParams.Paths),
		jobs:              configParams.Jobs,
		upgradedToVersion: configParams.UpgradedToVersion,
		tag:               configParams.Tag,
		nonce:             configParams.Nonce,
		model:             configParams.Model,
		caCert:            configParams.CACert,
		oldPassword:       configParams.Password,
		values:            configParams.Values,
		preferIPv6:        configParams.PreferIPv6,
	}
	if len(configParams.StateAddresses) > 0 {
		config.stateDetails = &connectionDetails{
			addresses: configParams.StateAddresses,
		}
	}
	if len(configParams.APIAddresses) > 0 {
		config.apiDetails = &connectionDetails{
			addresses: configParams.APIAddresses,
		}
	}
	if err := config.check(); err != nil {
		return nil, err
	}
	if config.values == nil {
		config.values = make(map[string]string)
	}
	config.configFilePath = ConfigPath(config.paths.DataDir, config.tag)
	return config, nil
}
Beispiel #4
0
// SetFlags implements Command.Init.
func (c *useModelCommand) Init(args []string) error {
	if len(args) == 0 || strings.TrimSpace(args[0]) == "" {
		return errors.New("no model supplied")
	}

	name, args := args[0], args[1:]

	// First check to see if an owner has been specified.
	bits := strings.SplitN(name, "/", 2)
	switch len(bits) {
	case 1:
		// No user specified
		c.ModelName = bits[0]
	case 2:
		owner := bits[0]
		if names.IsValidUser(owner) {
			c.Owner = owner
		} else {
			return errors.Errorf("%q is not a valid user", owner)
		}
		c.ModelName = bits[1]
	}

	// Model names can generally be anything, but we take a good
	// stab at trying to determine if the user has specified a UUID
	// instead of a name. For now, we only accept a properly formatted UUID,
	// which means one with dashes in the right place.
	if names.IsValidModel(c.ModelName) {
		c.ModelUUID, c.ModelName = c.ModelName, ""
	}

	return cmd.CheckEmpty(args)
}
Beispiel #5
0
// validateModelUUID is the common validator for the various
// apiserver components that need to check for a valid model
// UUID.  An empty modelUUID means that the connection has come in at
// the root of the URL space and refers to the controller
// model.
//
// It returns the validated model UUID.
func validateModelUUID(args validateArgs) (string, error) {
	ssState := args.statePool.SystemState()
	if args.modelUUID == "" {
		// We allow the modelUUID to be empty for 2 cases
		// 1) Compatibility with older clients
		// 2) TODO: server a limited API at the root (empty modelUUID)
		//    with just the user manager and model manager
		//    if the connection comes over a sufficiently up to date
		//    login command.
		if args.strict {
			return "", errors.Trace(common.UnknownModelError(args.modelUUID))
		}
		logger.Debugf("validate model uuid: empty modelUUID")
		return ssState.ModelUUID(), nil
	}
	if args.modelUUID == ssState.ModelUUID() {
		logger.Debugf("validate model uuid: controller model - %s", args.modelUUID)
		return args.modelUUID, nil
	}
	if args.controllerModelOnly {
		return "", errors.Unauthorizedf("requested model %q is not the controller model", args.modelUUID)
	}
	if !names.IsValidModel(args.modelUUID) {
		return "", errors.Trace(common.UnknownModelError(args.modelUUID))
	}
	modelTag := names.NewModelTag(args.modelUUID)
	if _, err := ssState.GetModel(modelTag); err != nil {
		return "", errors.Wrap(err, common.UnknownModelError(args.modelUUID))
	}
	logger.Debugf("validate model uuid: %s", args.modelUUID)
	return args.modelUUID, nil
}
Beispiel #6
0
// WrapAgent wraps an agent.Agent (expected to be a machine agent, fwiw)
// such that its references the supplied model rather than the controller
// model; its config is immutable; and it doesn't use OldPassword.
//
// It's a strong sign that the agent package needs some work...
func WrapAgent(a agent.Agent, uuid string) (agent.Agent, error) {
	if !names.IsValidModel(uuid) {
		return nil, errors.NotValidf("model uuid %q", uuid)
	}
	return &modelAgent{
		Agent: a,
		uuid:  uuid,
	}, nil
}
Beispiel #7
0
func dropModelUUID(id string) string {
	fullID := id
	parts := strings.SplitN(fullID, ":", 2)
	if len(parts) == 2 {
		if names.IsValidModel(parts[0]) {
			fullID = parts[1]
		}
	}
	return fullID
}
Beispiel #8
0
// Validate performs sanity checks on the migration configuration it
// holds.
func (s *ModelMigrationSpec) Validate() error {
	if !names.IsValidModel(s.ModelUUID) {
		return errors.NotValidf("model UUID")
	}
	if !names.IsValidModel(s.TargetControllerUUID) {
		return errors.NotValidf("controller UUID")
	}
	if len(s.TargetAddrs) < 1 {
		return errors.NotValidf("empty target API addresses")
	}
	if s.TargetCACert == "" {
		return errors.NotValidf("empty target CA cert")
	}
	if !names.IsValidUser(s.TargetUser) {
		return errors.NotValidf("target user")
	}
	if s.TargetPassword == "" {
		return errors.NotValidf("empty target password")
	}
	return nil
}
Beispiel #9
0
// cacheAPIInfo updates the local environment settings (.jenv file)
// with the provided apiInfo, assuming we've just successfully
// connected to the API server.
func cacheAPIInfo(st api.Connection, info configstore.EnvironInfo, apiInfo *api.Info) (err error) {
	defer errors.DeferredAnnotatef(&err, "failed to cache API credentials")
	var modelUUID string
	if names.IsValidModel(apiInfo.ModelTag.Id()) {
		modelUUID = apiInfo.ModelTag.Id()
	} else {
		// For backwards-compatibility, we have to allow connections
		// with an empty UUID. Login will work for the same reasons.
		logger.Warningf("ignoring invalid cached API endpoint model UUID %v", apiInfo.ModelTag.Id())
	}
	hostPorts, err := network.ParseHostPorts(apiInfo.Addrs...)
	if err != nil {
		return errors.Annotatef(err, "invalid API addresses %v", apiInfo.Addrs)
	}
	addrConnectedTo, err := network.ParseHostPorts(st.Addr())
	if err != nil {
		// Should never happen, since we've just connected with it.
		return errors.Annotatef(err, "invalid API address %q", st.Addr())
	}
	addrs, hostnames, addrsChanged := PrepareEndpointsForCaching(
		info, [][]network.HostPort{hostPorts}, addrConnectedTo[0],
	)

	endpoint := configstore.APIEndpoint{
		CACert:    string(apiInfo.CACert),
		ModelUUID: modelUUID,
	}
	if addrsChanged {
		endpoint.Addresses = addrs
		endpoint.Hostnames = hostnames
	}
	info.SetAPIEndpoint(endpoint)
	tag, ok := apiInfo.Tag.(names.UserTag)
	if !ok {
		return errors.Errorf("apiInfo.Tag was of type %T, expecting names.UserTag", apiInfo.Tag)
	}
	info.SetAPICredentials(configstore.APICredentials{
		// This looks questionable. We have a tag, say "user-admin", but then only
		// the Id portion of the tag is recorded, "admin", so this is really a
		// username, not a tag, and cannot be reconstructed accurately.
		User:     tag.Id(),
		Password: apiInfo.Password,
	})
	return info.Write()
}
Beispiel #10
0
func (c *Client) modifyModelUser(action params.ModelAction, user, access string, modelUUIDs []string) error {
	var args params.ModifyModelAccessRequest

	if !names.IsValidUser(user) {
		return errors.Errorf("invalid username: %q", user)
	}
	userTag := names.NewUserTag(user)

	accessPermission, err := ParseModelAccess(access)
	if err != nil {
		return errors.Trace(err)
	}
	for _, model := range modelUUIDs {
		if !names.IsValidModel(model) {
			return errors.Errorf("invalid model: %q", model)
		}
		modelTag := names.NewModelTag(model)
		args.Changes = append(args.Changes, params.ModifyModelAccess{
			UserTag:  userTag.String(),
			Action:   action,
			Access:   accessPermission,
			ModelTag: modelTag.String(),
		})
	}

	var result params.ErrorResults
	err = c.facade.FacadeCall("ModifyModelAccess", args, &result)
	if err != nil {
		return errors.Trace(err)
	}
	if len(result.Results) != len(args.Changes) {
		return errors.Errorf("expected %d results, got %d", len(args.Changes), len(result.Results))
	}

	for i, r := range result.Results {
		if r.Error != nil && r.Error.Code == params.CodeAlreadyExists {
			logger.Warningf("model %q is already shared with %q", modelUUIDs[i], userTag.Canonical())
			result.Results[i].Error = nil
		}
	}
	return result.Combine()
}
Beispiel #11
0
// Load causes all recorded collections to be created and indexed as specified;
// the returned Database will filter queries and transactions according to the
// suppplied model UUID.
func (schema collectionSchema) Load(db *mgo.Database, modelUUID string) (Database, error) {
	if !names.IsValidModel(modelUUID) {
		return nil, errors.New("invalid model UUID")
	}
	for name, info := range schema {
		rawCollection := db.C(name)
		if spec := info.explicitCreate; spec != nil {
			if err := createCollection(rawCollection, spec); err != nil {
				message := fmt.Sprintf("cannot create collection %q", name)
				return nil, maybeUnauthorized(err, message)
			}
		}
		for _, index := range info.indexes {
			if err := rawCollection.EnsureIndex(index); err != nil {
				return nil, maybeUnauthorized(err, "cannot create index")
			}
		}
	}
	return &database{
		raw:       db,
		schema:    schema,
		modelUUID: modelUUID,
	}, nil
}