Example #1
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.Environment.Id(); uuid == "" {
		return nil, errors.Trace(requiredError("environment"))
	} else if !names.IsValidEnvironment(uuid) {
		return nil, errors.Errorf("%q is not a valid environment 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,
		environment:       configParams.Environment,
		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
}
Example #2
0
// SetFlags implements Command.Init.
func (c *useEnvironmentCommand) Init(args []string) error {
	if len(args) == 0 || strings.TrimSpace(args[0]) == "" {
		return errors.New("no environment 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.EnvName = 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.EnvName = bits[1]
	}

	// Environment 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.IsValidEnvironment(c.EnvName) {
		c.EnvUUID, c.EnvName = c.EnvName, ""
	}

	return cmd.CheckEmpty(args)
}
Example #3
0
// apiInfoConnect looks for endpoint on the given environment and
// tries to connect to it, sending the result on the returned channel.
func apiInfoConnect(store configstore.Storage, info configstore.EnvironInfo, apiOpen apiOpenFunc, stop <-chan struct{}) (apiState, 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 environTag names.EnvironTag
	if names.IsValidEnvironment(endpoint.EnvironUUID) {
		environTag = names.NewEnvironTag(endpoint.EnvironUUID)
	} else {
		// For backwards-compatibility, we have to allow connections
		// with an empty UUID. Login will work for the same reasons.
		logger.Warningf("ignoring invalid API endpoint environment UUID %v", endpoint.EnvironUUID)
	}
	apiInfo := &api.Info{
		Addrs:      endpoint.Addresses,
		CACert:     endpoint.CACert,
		Tag:        environInfoUserTag(info),
		Password:   info.APICredentials().Password,
		EnvironTag: environTag,
	}
	st, err := apiOpen(apiInfo, api.DefaultDialOpts())
	if err != nil {
		return nil, &infoConnectError{err}
	}
	return st, nil
}
Example #4
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 environTag names.EnvironTag
	if names.IsValidEnvironment(endpoint.EnvironUUID) {
		environTag = names.NewEnvironTag(endpoint.EnvironUUID)
	}

	apiInfo := &api.Info{
		Addrs:      endpoint.Addresses,
		CACert:     endpoint.CACert,
		Tag:        environInfoUserTag(info),
		Password:   info.APICredentials().Password,
		EnvironTag: environTag,
	}
	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
}
Example #5
0
// validateEnvironUUID is the common validator for the various
// apiserver components that need to check for a valid environment
// UUID.  An empty envUUID means that the connection has come in at
// the root of the URL space and refers to the state server
// environment.
//
// It returns the validated environment UUID.
func validateEnvironUUID(args validateArgs) (string, error) {
	ssState := args.statePool.SystemState()
	if args.envUUID == "" {
		// We allow the environUUID to be empty for 2 cases
		// 1) Compatibility with older clients
		// 2) TODO: server a limited API at the root (empty envUUID)
		//    with just the user manager and environment manager
		//    if the connection comes over a sufficiently up to date
		//    login command.
		if args.strict {
			return "", errors.Trace(common.UnknownEnvironmentError(args.envUUID))
		}
		logger.Debugf("validate env uuid: empty envUUID")
		return ssState.EnvironUUID(), nil
	}
	if args.envUUID == ssState.EnvironUUID() {
		logger.Debugf("validate env uuid: state server environment - %s", args.envUUID)
		return args.envUUID, nil
	}
	if args.stateServerEnvOnly {
		return "", errors.Unauthorizedf("requested environment %q is not the state server environment", args.envUUID)
	}
	if !names.IsValidEnvironment(args.envUUID) {
		return "", errors.Trace(common.UnknownEnvironmentError(args.envUUID))
	}
	envTag := names.NewEnvironTag(args.envUUID)
	if _, err := ssState.GetEnvironment(envTag); err != nil {
		return "", errors.Wrap(err, common.UnknownEnvironmentError(args.envUUID))
	}
	logger.Debugf("validate env uuid: %s", args.envUUID)
	return args.envUUID, nil
}
Example #6
0
func dropEnvUUID(id string) string {
	fullID := id
	parts := strings.SplitN(fullID, ":", 2)
	if len(parts) == 2 {
		if names.IsValidEnvironment(parts[0]) {
			fullID = parts[1]
		}
	}
	return fullID
}
Example #7
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 apiState, info configstore.EnvironInfo, apiInfo *api.Info) (err error) {
	defer errors.DeferredAnnotatef(&err, "failed to cache API credentials")
	var environUUID string
	if names.IsValidEnvironment(apiInfo.EnvironTag.Id()) {
		environUUID = apiInfo.EnvironTag.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 environment UUID %v", apiInfo.EnvironTag.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),
		EnvironUUID: environUUID,
	}
	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()
}
Example #8
0
// validateEnvironUUID is the common validator for the various apiserver
// components that need to check for a valid environment UUID.
// An empty envUUID means that the connection has come in at the root
// of the URL space and refers to the state server environment.
// The *state.State parameter is expected to be the state server State
// connection.  The return *state.State is a connection for the specified
// environment UUID if the UUID refers to an environment contained in the
// database.  If the bool return value is true, the state connection must
// be closed by the caller at the end of serving the client connection.
func validateEnvironUUID(args validateArgs) (*state.State, bool, error) {
	if args.envUUID == "" {
		// We allow the environUUID to be empty for 2 cases
		// 1) Compatibility with older clients
		// 2) TODO: server a limited API at the root (empty envUUID)
		//    with just the user manager and environment manager
		//    if the connection comes over a sufficiently up to date
		//    login command.
		if args.strict {
			return nil, false, errors.Trace(common.UnknownEnvironmentError(args.envUUID))
		}
		logger.Debugf("validate env uuid: empty envUUID")
		return args.st, false, nil
	}
	if args.envUUID == args.st.EnvironUUID() {
		logger.Debugf("validate env uuid: state server environment - %s", args.envUUID)
		return args.st, false, nil
	}
	if args.stateServerEnvOnly {
		return nil, false, errors.Unauthorizedf("requested environment %q is not the state server environment", args.envUUID)
	}
	if !names.IsValidEnvironment(args.envUUID) {
		return nil, false, errors.Trace(common.UnknownEnvironmentError(args.envUUID))
	}
	envTag := names.NewEnvironTag(args.envUUID)
	if env, err := args.st.GetEnvironment(envTag); err != nil {
		return nil, false, errors.Wrap(err, common.UnknownEnvironmentError(args.envUUID))
	} else if env.Life() != state.Alive {
		return nil, false, errors.Errorf("environment %q is no longer live", args.envUUID)
	}
	logger.Debugf("validate env uuid: %s", args.envUUID)
	result, err := args.st.ForEnviron(envTag)
	if err != nil {
		return nil, false, errors.Trace(err)
	}
	return result, true, nil
}
Example #9
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 apiOpenFunc, stop <-chan struct{}) (apiState, 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 environTag names.EnvironTag
	if names.IsValidEnvironment(endpoint.EnvironUUID) {
		environTag = names.NewEnvironTag(endpoint.EnvironUUID)
	}

	apiInfo := &api.Info{
		Addrs:      endpoint.Addresses,
		CACert:     endpoint.CACert,
		Tag:        environInfoUserTag(info),
		Password:   info.APICredentials().Password,
		EnvironTag: environTag,
	}
	st, err := apiOpen(apiInfo, api.DefaultDialOpts())
	if err != nil {
		return nil, &infoConnectError{err}
	}
	return st, nil
}
Example #10
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 environment UUID.
func (schema collectionSchema) Load(db *mgo.Database, environUUID string) (Database, error) {
	if !names.IsValidEnvironment(environUUID) {
		return nil, errors.New("invalid environment 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,
		environUUID: environUUID,
	}, nil
}