Example #1
0
func (c *MigrateCommand) Init(args []string) error {
	if len(args) == 0 {
		return errors.New("missing operation")
	}
	c.operation, args = args[0], args[1:]

	switch c.operation {
	case "export":
		if len(args) == 0 {
			return errors.New("missing model uuid")
		}
		c.modelUUID, args = args[0], args[1:]

	case "import":
		if len(args) == 0 {
			return errors.New("missing yaml filename")
		}
		c.filename, args = args[0], args[1:]
	default:
		return errors.Errorf("unknown operation %q", c.operation)
	}

	if !names.IsValidMachine(c.machineId) {
		return errors.Errorf("%q is not a valid machine id", c.machineId)
	}
	c.machineTag = names.NewMachineTag(c.machineId)
	return cmd.CheckEmpty(args)
}
Example #2
0
func (s *machineSuite) TestMachineIdFormats(c *gc.C) {
	for i, test := range machineIdTests {
		c.Logf("test %d: %q", i, test.pattern)
		c.Assert(names.IsValidMachine(test.pattern), gc.Equals, test.valid)
		c.Assert(names.IsContainerMachine(test.pattern), gc.Equals, test.container)
	}
}
Example #3
0
// Init is called by the cmd system to initialize the structure for
// running.
func (a *machineAgentCmd) Init(args []string) error {

	if !names.IsValidMachine(a.machineId) {
		return fmt.Errorf("--machine-id option must be set, and expects a non-negative integer")
	}
	if err := a.agentInitializer.CheckArgs(args); err != nil {
		return err
	}

	// Due to changes in the logging, and needing to care about old
	// models that have been upgraded, we need to explicitly remove the
	// file writer if one has been added, otherwise we will get duplicate
	// lines of all logging in the log file.
	loggo.RemoveWriter("logfile")

	if a.logToStdErr {
		return nil
	}

	err := a.currentConfig.ReadConfig(names.NewMachineTag(a.machineId).String())
	if err != nil {
		return errors.Annotate(err, "cannot read agent configuration")
	}

	// the context's stderr is set as the loggo writer in github.com/juju/cmd/logging.go
	a.ctx.Stderr = &lumberjack.Logger{
		Filename:   agent.LogFilename(a.currentConfig.CurrentConfig()),
		MaxSize:    300, // megabytes
		MaxBackups: 2,
	}

	return nil
}
Example #4
0
// AddUnits starts n units of the given service and allocates machines
// to them as necessary.
func AddUnits(st *state.State, svc *state.Service, n int, machineIdSpec string) ([]*state.Unit, error) {
	if machineIdSpec != "" && n != 1 {
		return nil, errors.Errorf("cannot add multiple units of service %q to a single machine", svc.Name())
	}
	var placement []*instance.Placement
	if machineIdSpec != "" {
		mid := machineIdSpec
		scope := instance.MachineScope
		var containerType instance.ContainerType
		specParts := strings.SplitN(machineIdSpec, ":", 2)
		if len(specParts) > 1 {
			firstPart := specParts[0]
			var err error
			if containerType, err = instance.ParseContainerType(firstPart); err == nil {
				mid = specParts[1]
				scope = string(containerType)
			}
		}
		if !names.IsValidMachine(mid) {
			return nil, fmt.Errorf("invalid force machine id %q", mid)
		}
		placement = []*instance.Placement{
			{
				Scope:     scope,
				Directive: mid,
			},
		}
	}
	return AddUnitsWithPlacement(st, svc, n, placement)
}
Example #5
0
// PrivateAddress implements the server side of Client.PrivateAddress.
func (c *Client) PrivateAddress(p params.PrivateAddress) (results params.PrivateAddressResults, err error) {
	switch {
	case names.IsValidMachine(p.Target):
		machine, err := c.api.stateAccessor.Machine(p.Target)
		if err != nil {
			return results, err
		}
		addr, err := machine.PrivateAddress()
		if err != nil {
			return results, errors.Annotatef(err, "error fetching address for machine %q", machine)
		}
		return params.PrivateAddressResults{PrivateAddress: addr.Value}, nil

	case names.IsValidUnit(p.Target):
		unit, err := c.api.stateAccessor.Unit(p.Target)
		if err != nil {
			return results, err
		}
		addr, err := unit.PrivateAddress()
		if err != nil {
			return results, errors.Annotatef(err, "error fetching address for unit %q", unit)
		}
		return params.PrivateAddressResults{PrivateAddress: addr.Value}, nil
	}
	return results, fmt.Errorf("unknown unit or machine %q", p.Target)

}
Example #6
0
File: ssh.go Project: kapilt/juju
func (c *SSHCommon) hostFromTarget(target string) (string, error) {
	// If the target is neither a machine nor a unit,
	// assume it's a hostname and try it directly.
	if !names.IsValidMachine(target) && !names.IsValidUnit(target) {
		return target, nil
	}
	// A target may not initially have an address (e.g. the
	// address updater hasn't yet run), so we must do this in
	// a loop.
	if _, err := c.ensureAPIClient(); err != nil {
		return "", err
	}
	var err error
	for a := sshHostFromTargetAttemptStrategy.Start(); a.Next(); {
		var addr string
		if c.proxy {
			addr, err = c.apiClient.PrivateAddress(target)
		} else {
			addr, err = c.apiClient.PublicAddress(target)
		}
		if err == nil {
			return addr, nil
		}
	}
	return "", err
}
Example #7
0
// PrivateAddress implements the server side of Client.PrivateAddress.
func (c *Client) PrivateAddress(p params.PrivateAddress) (results params.PrivateAddressResults, err error) {
	switch {
	case names.IsValidMachine(p.Target):
		machine, err := c.api.state.Machine(p.Target)
		if err != nil {
			return results, err
		}
		addr := network.SelectInternalAddress(machine.Addresses(), false)
		if addr == "" {
			return results, fmt.Errorf("machine %q has no internal address", machine)
		}
		return params.PrivateAddressResults{PrivateAddress: addr}, nil

	case names.IsValidUnit(p.Target):
		unit, err := c.api.state.Unit(p.Target)
		if err != nil {
			return results, err
		}
		addr, ok := unit.PrivateAddress()
		if !ok {
			return results, fmt.Errorf("unit %q has no internal address", unit)
		}
		return params.PrivateAddressResults{PrivateAddress: addr}, nil
	}
	return results, fmt.Errorf("unknown unit or machine %q", p.Target)
}
Example #8
0
// addServiceUnits adds a given number of units to a service.
func addServiceUnits(state *state.State, args params.AddServiceUnits) ([]*state.Unit, error) {
	service, err := state.Service(args.ServiceName)
	if err != nil {
		return nil, err
	}
	if args.NumUnits < 1 {
		return nil, fmt.Errorf("must add at least one unit")
	}

	// New API uses placement directives.
	if len(args.Placement) > 0 {
		return jjj.AddUnitsWithPlacement(state, service, args.NumUnits, args.Placement)
	}

	// Otherwise we use the older machine spec.
	if args.NumUnits > 1 && args.ToMachineSpec != "" {
		return nil, fmt.Errorf("cannot use NumUnits with ToMachineSpec")
	}

	if args.ToMachineSpec != "" && names.IsValidMachine(args.ToMachineSpec) {
		_, err = state.Machine(args.ToMachineSpec)
		if err != nil {
			return nil, errors.Annotatef(err, `cannot add units for service "%v" to machine %v`, args.ServiceName, args.ToMachineSpec)
		}
	}
	return jjj.AddUnits(state, service, args.NumUnits, args.ToMachineSpec)
}
Example #9
0
// makePlacement makes a placement directive for the given machineIdSpec.
func makePlacement(machineIdSpec string) ([]*instance.Placement, error) {
	if machineIdSpec == "" {
		return nil, nil
	}
	mid := machineIdSpec
	scope := instance.MachineScope
	var containerType instance.ContainerType
	specParts := strings.SplitN(machineIdSpec, ":", 2)
	if len(specParts) > 1 {
		firstPart := specParts[0]
		var err error
		if containerType, err = instance.ParseContainerType(firstPart); err == nil {
			mid = specParts[1]
			scope = string(containerType)
		}
	}
	if !names.IsValidMachine(mid) {
		return nil, errors.Errorf("invalid force machine id %q", mid)
	}
	return []*instance.Placement{
		{
			Scope:     scope,
			Directive: mid,
		},
	}, nil
}
Example #10
0
// ParseFilesystemAttachmentId parses a string as a filesystem attachment ID,
// returning the machine and filesystem components.
func ParseFilesystemAttachmentId(id string) (names.MachineTag, names.FilesystemTag, error) {
	fields := strings.SplitN(id, ":", 2)
	if len(fields) != 2 || !names.IsValidMachine(fields[0]) || !names.IsValidFilesystem(fields[1]) {
		return names.MachineTag{}, names.FilesystemTag{}, errors.Errorf("invalid filesystem attachment ID %q", id)
	}
	machineTag := names.NewMachineTag(fields[0])
	filesystemTag := names.NewFilesystemTag(fields[1])
	return machineTag, filesystemTag, nil
}
Example #11
0
// ParseVolumeAttachmentId parses a string as a volume attachment ID,
// returning the machine and volume components.
func ParseVolumeAttachmentId(id string) (names.MachineTag, names.VolumeTag, error) {
	fields := strings.SplitN(id, ":", 2)
	if len(fields) != 2 || !names.IsValidMachine(fields[0]) || !names.IsValidVolume(fields[1]) {
		return names.MachineTag{}, names.VolumeTag{}, errors.Errorf("invalid volume attachment ID %q", id)
	}
	machineTag := names.NewMachineTag(fields[0])
	volumeTag := names.NewVolumeTag(fields[1])
	return machineTag, volumeTag, nil
}
Example #12
0
func targetToTag(target string) (names.Tag, error) {
	switch {
	case names.IsValidMachine(target):
		return names.NewMachineTag(target), nil
	case names.IsValidUnit(target):
		return names.NewUnitTag(target), nil
	default:
		return nil, errors.NotValidf("target %q", target)
	}
}
Example #13
0
func (c *removeCommand) Init(args []string) error {
	if len(args) == 0 {
		return fmt.Errorf("no machines specified")
	}
	for _, id := range args {
		if !names.IsValidMachine(id) {
			return fmt.Errorf("invalid machine id %q", id)
		}
	}
	c.MachineIds = args
	return nil
}
Example #14
0
// Init initializes the command for running.
func (a *MachineAgent) Init(args []string) error {
	if !names.IsValidMachine(a.MachineId) {
		return fmt.Errorf("--machine-id option must be set, and expects a non-negative integer")
	}
	if err := a.AgentConf.CheckArgs(args); err != nil {
		return err
	}
	a.runner = newRunner(isFatal, moreImportant)
	a.workersStarted = make(chan struct{})
	a.upgradeWorkerContext = NewUpgradeWorkerContext()
	return nil
}
Example #15
0
func (c *FixitCommand) Init(args []string) error {
	if len(args) == 0 {
		return errors.New("missing machine-id")
	}
	var machineId string
	machineId, args = args[0], args[1:]

	if !names.IsValidMachine(machineId) {
		return errors.Errorf("%q is not a valid machine id", machineId)
	}
	c.machineTag = names.NewMachineTag(machineId)
	return cmd.CheckEmpty(args)
}
Example #16
0
func (c *RetryProvisioningCommand) Init(args []string) error {
	if len(args) == 0 {
		return fmt.Errorf("no machine specified")
	}
	c.Machines = make([]names.MachineTag, len(args))
	for i, arg := range args {
		if !names.IsValidMachine(arg) {
			return fmt.Errorf("invalid machine %q", arg)
		}
		c.Machines[i] = names.NewMachineTag(arg)
	}
	return nil
}
Example #17
0
// ParsePlacement attempts to parse the specified string and create a
// corresponding Placement structure.
//
// If the placement directive is non-empty and missing a scope,
// ErrPlacementScopeMissing will be returned as well as a Placement
// with an empty Scope field.
func ParsePlacement(directive string) (*Placement, error) {
	if directive == "" {
		return nil, nil
	}
	if colon := strings.IndexRune(directive, ':'); colon != -1 {
		scope, directive := directive[:colon], directive[colon+1:]
		if scope == "" {
			return nil, ErrPlacementScopeMissing
		}
		// Sanity check: machine/container scopes require a machine ID as the value.
		if (scope == MachineScope || isContainerType(scope)) && !names.IsValidMachine(directive) {
			return nil, fmt.Errorf("invalid value %q for %q scope: expected machine-id", directive, scope)
		}
		return &Placement{Scope: scope, Directive: directive}, nil
	}
	if names.IsValidMachine(directive) {
		return &Placement{Scope: MachineScope, Directive: directive}, nil
	}
	if isContainerType(directive) {
		return &Placement{Scope: directive}, nil
	}
	return nil, ErrPlacementScopeMissing
}
Example #18
0
func matchMachineId(m *state.Machine, patterns []string) (bool, bool, error) {
	var anyValid bool
	for _, p := range patterns {
		if !names.IsValidMachine(p) {
			continue
		}
		anyValid = true
		if m.Id() == p || strings.HasPrefix(m.Id(), p+"/") {
			// Pattern matches the machine, or container's
			// host machine.
			return true, true, nil
		}
	}
	return false, anyValid, nil
}
Example #19
0
File: api.go Project: pmatulis/juju
// NewAPI returns a new API client for the Singular facade. It exposes methods
// for claiming and observing administration responsibility for the apiCaller's
// model, on behalf of the supplied controller machine.
func NewAPI(apiCaller base.APICaller, controllerTag names.MachineTag) (*API, error) {
	controllerId := controllerTag.Id()
	if !names.IsValidMachine(controllerId) {
		return nil, errors.NotValidf("controller tag")
	}
	modelTag, err := apiCaller.ModelTag()
	if err != nil {
		return nil, errors.Trace(err)
	}
	facadeCaller := base.NewFacadeCaller(apiCaller, "Singular")
	return &API{
		modelTag:      modelTag,
		controllerTag: controllerTag,
		facadeCaller:  facadeCaller,
	}, nil
}
Example #20
0
func (c *retryProvisioningCommand) Init(args []string) error {
	if len(args) == 0 {
		return errors.Errorf("no machine specified")
	}
	c.Machines = make([]names.MachineTag, len(args))
	for i, arg := range args {
		if !names.IsValidMachine(arg) {
			return errors.Errorf("invalid machine %q", arg)
		}
		if names.IsContainerMachine(arg) {
			return errors.Errorf("invalid machine %q retry-provisioning does not support containers", arg)
		}
		c.Machines[i] = names.NewMachineTag(arg)
	}
	return nil
}
Example #21
0
func (c *RunCommand) Init(args []string) error {
	if len(args) == 0 {
		return fmt.Errorf("no commands specified")
	}
	c.commands, args = args[0], args[1:]

	if c.all {
		if len(c.machines) != 0 {
			return fmt.Errorf("You cannot specify --all and individual machines")
		}
		if len(c.services) != 0 {
			return fmt.Errorf("You cannot specify --all and individual services")
		}
		if len(c.units) != 0 {
			return fmt.Errorf("You cannot specify --all and individual units")
		}
	} else {
		if len(c.machines) == 0 && len(c.services) == 0 && len(c.units) == 0 {
			return fmt.Errorf("You must specify a target, either through --all, --machine, --service or --unit")
		}
	}

	var nameErrors []string
	for _, machineId := range c.machines {
		if !names.IsValidMachine(machineId) {
			nameErrors = append(nameErrors, fmt.Sprintf("  %q is not a valid machine id", machineId))
		}
	}
	for _, service := range c.services {
		if !names.IsValidService(service) {
			nameErrors = append(nameErrors, fmt.Sprintf("  %q is not a valid service name", service))
		}
	}
	for _, unit := range c.units {
		if !names.IsValidUnit(unit) {
			nameErrors = append(nameErrors, fmt.Sprintf("  %q is not a valid unit name", unit))
		}
	}
	if len(nameErrors) > 0 {
		return fmt.Errorf("The following run targets are not valid:\n%s",
			strings.Join(nameErrors, "\n"))
	}

	return cmd.CheckEmpty(args)
}
Example #22
0
// addServiceUnits adds a given number of units to a service.
func addServiceUnits(state *state.State, args params.AddServiceUnits) ([]*state.Unit, error) {
	service, err := state.Service(args.ServiceName)
	if err != nil {
		return nil, err
	}
	if args.NumUnits < 1 {
		return nil, fmt.Errorf("must add at least one unit")
	}
	if args.NumUnits > 1 && args.ToMachineSpec != "" {
		return nil, fmt.Errorf("cannot use NumUnits with ToMachineSpec")
	}

	if args.ToMachineSpec != "" && names.IsValidMachine(args.ToMachineSpec) {
		_, err = state.Machine(args.ToMachineSpec)
		if err != nil {
			return nil, errors.Annotatef(err, `cannot add units for service "%v" to machine %v`, args.ServiceName, args.ToMachineSpec)
		}
	}
	return jjj.AddUnits(state, service, args.NumUnits, args.ToMachineSpec)
}
Example #23
0
// Run the commands specified on the machines identified through the
// list of machines, units and services.
func (a *ActionAPI) Run(run params.RunParams) (results params.ActionResults, err error) {
	if err := a.check.ChangeAllowed(); err != nil {
		return results, errors.Trace(err)
	}

	units, err := getAllUnitNames(a.state, run.Units, run.Services)
	if err != nil {
		return results, errors.Trace(err)
	}

	machines := make([]names.Tag, len(run.Machines))
	for i, machineId := range run.Machines {
		if !names.IsValidMachine(machineId) {
			return results, errors.Errorf("invalid machine id %q", machineId)
		}
		machines[i] = names.NewMachineTag(machineId)
	}

	actionParams := a.createActionsParams(append(units, machines...), run.Commands, run.Timeout)

	return queueActions(a, actionParams)
}
Example #24
0
// Init implements cmd.Command.
func (c *dumpLogsCommand) Init(args []string) error {
	err := c.agentConfig.CheckArgs(args)
	if err != nil {
		return errors.Trace(err)
	}

	if c.machineId == "" {
		machineId, err := c.findMachineId(c.agentConfig.DataDir())
		if err != nil {
			return errors.Trace(err)
		}
		c.machineId = machineId
	} else if !names.IsValidMachine(c.machineId) {
		return errors.New("--machine-id option expects a non-negative integer")
	}

	err = c.agentConfig.ReadConfig(names.NewMachineTag(c.machineId).String())
	if err != nil {
		return errors.Trace(err)
	}

	return nil
}
Example #25
0
func (cfg *InstanceConfig) VerifyConfig() (err error) {
	defer errors.DeferredAnnotatef(&err, "invalid machine configuration")
	if !names.IsValidMachine(cfg.MachineId) {
		return errors.New("invalid machine id")
	}
	if cfg.DataDir == "" {
		return errors.New("missing var directory")
	}
	if cfg.LogDir == "" {
		return errors.New("missing log directory")
	}
	if cfg.MetricsSpoolDir == "" {
		return errors.New("missing metrics spool directory")
	}
	if len(cfg.Jobs) == 0 {
		return errors.New("missing machine jobs")
	}
	if cfg.CloudInitOutputLog == "" {
		return errors.New("missing cloud-init output log path")
	}
	if cfg.tools == nil {
		// SetTools() has never been called successfully.
		return errors.New("missing tools")
	}
	// We don't need to check cfg.toolsURLs since SetTools() does.
	if cfg.MongoInfo == nil {
		return errors.New("missing state info")
	}
	if len(cfg.MongoInfo.CACert) == 0 {
		return errors.New("missing CA certificate")
	}
	if cfg.APIInfo == nil {
		return errors.New("missing API info")
	}
	if cfg.APIInfo.ModelTag.Id() == "" {
		return errors.New("missing model tag")
	}
	if len(cfg.APIInfo.CACert) == 0 {
		return errors.New("missing API CA certificate")
	}
	if cfg.MachineAgentServiceName == "" {
		return errors.New("missing machine agent service name")
	}
	if cfg.Bootstrap {
		if cfg.Config == nil {
			return errors.New("missing model configuration")
		}
		if cfg.MongoInfo.Tag != nil {
			return errors.New("entity tag must be nil when starting a controller")
		}
		if cfg.APIInfo.Tag != nil {
			return errors.New("entity tag must be nil when starting a controller")
		}
		if cfg.StateServingInfo == nil {
			return errors.New("missing state serving info")
		}
		if len(cfg.StateServingInfo.Cert) == 0 {
			return errors.New("missing controller certificate")
		}
		if len(cfg.StateServingInfo.PrivateKey) == 0 {
			return errors.New("missing controller private key")
		}
		if len(cfg.StateServingInfo.CAPrivateKey) == 0 {
			return errors.New("missing ca cert private key")
		}
		if cfg.StateServingInfo.StatePort == 0 {
			return errors.New("missing state port")
		}
		if cfg.StateServingInfo.APIPort == 0 {
			return errors.New("missing API port")
		}
		if cfg.InstanceId == "" {
			return errors.New("missing instance-id")
		}
		if len(cfg.HostedModelConfig) == 0 {
			return errors.New("missing hosted model config")
		}
	} else {
		if len(cfg.MongoInfo.Addrs) == 0 {
			return errors.New("missing state hosts")
		}
		if cfg.MongoInfo.Tag != names.NewMachineTag(cfg.MachineId) {
			return errors.New("entity tag must match started machine")
		}
		if len(cfg.APIInfo.Addrs) == 0 {
			return errors.New("missing API hosts")
		}
		if cfg.APIInfo.Tag != names.NewMachineTag(cfg.MachineId) {
			return errors.New("entity tag must match started machine")
		}
		if cfg.StateServingInfo != nil {
			return errors.New("state serving info unexpectedly present")
		}
		if len(cfg.HostedModelConfig) != 0 {
			return errors.New("hosted model config unexpectedly present")
		}
	}
	if cfg.MachineNonce == "" {
		return errors.New("missing machine nonce")
	}
	return nil
}
Example #26
0
// DeployService fetches the charm from the charm store and deploys it.
// The logic has been factored out into a common function which is called by
// both the legacy API on the client facade, as well as the new service facade.
func DeployService(st *state.State, owner string, args params.ServiceDeploy) error {
	curl, err := charm.ParseURL(args.CharmUrl)
	if err != nil {
		return errors.Trace(err)
	}
	if curl.Revision < 0 {
		return errors.Errorf("charm url must include revision")
	}

	if args.ToMachineSpec != "" && names.IsValidMachine(args.ToMachineSpec) {
		_, err = st.Machine(args.ToMachineSpec)
		if err != nil {
			return errors.Annotatef(err, `cannot deploy "%v" to machine %v`, args.ServiceName, args.ToMachineSpec)
		}
	}

	// Try to find the charm URL in state first.
	ch, err := st.Charm(curl)
	if errors.IsNotFound(err) {
		// Clients written to expect 1.16 compatibility require this next block.
		if curl.Schema != "cs" {
			return errors.Errorf(`charm url has unsupported schema %q`, curl.Schema)
		}
		if err = AddCharmWithAuthorization(st, params.AddCharmWithAuthorization{
			URL: args.CharmUrl,
		}); err == nil {
			ch, err = st.Charm(curl)
		}
	}
	if err != nil {
		return errors.Trace(err)
	}

	var settings charm.Settings
	if len(args.ConfigYAML) > 0 {
		settings, err = ch.Config().ParseSettingsYAML([]byte(args.ConfigYAML), args.ServiceName)
	} else if len(args.Config) > 0 {
		// Parse config in a compatible way (see function comment).
		settings, err = parseSettingsCompatible(ch, args.Config)
	}
	if err != nil {
		return errors.Trace(err)
	}
	// Convert network tags to names for any given networks.
	requestedNetworks, err := networkTagsToNames(args.Networks)
	if err != nil {
		return errors.Trace(err)
	}

	_, err = jjj.DeployService(st,
		jjj.DeployServiceParams{
			ServiceName: args.ServiceName,
			// TODO(dfc) ServiceOwner should be a tag
			ServiceOwner:   owner,
			Charm:          ch,
			NumUnits:       args.NumUnits,
			ConfigSettings: settings,
			Constraints:    args.Constraints,
			ToMachineSpec:  args.ToMachineSpec,
			Networks:       requestedNetworks,
			Storage:        args.Storage,
		})
	return err
}
Example #27
0
// AddUnits starts n units of the given service and allocates machines
// to them as necessary.
func AddUnits(st *state.State, svc *state.Service, n int, machineIdSpec string) ([]*state.Unit, error) {
	units := make([]*state.Unit, n)
	// Hard code for now till we implement a different approach.
	policy := state.AssignCleanEmpty
	// All units should have the same networks as the service.
	networks, err := svc.Networks()
	if err != nil {
		return nil, fmt.Errorf("cannot get service %q networks: %v", svc.Name(), err)
	}
	// TODO what do we do if we fail half-way through this process?
	for i := 0; i < n; i++ {
		unit, err := svc.AddUnit()
		if err != nil {
			return nil, fmt.Errorf("cannot add unit %d/%d to service %q: %v", i+1, n, svc.Name(), err)
		}
		if machineIdSpec != "" {
			if n != 1 {
				return nil, fmt.Errorf("cannot add multiple units of service %q to a single machine", svc.Name())
			}
			// machineIdSpec may be an existing machine or container, eg 3/lxc/2
			// or a new container on a machine, eg lxc:3
			mid := machineIdSpec
			var containerType instance.ContainerType
			specParts := strings.SplitN(machineIdSpec, ":", 2)
			if len(specParts) > 1 {
				firstPart := specParts[0]
				var err error
				if containerType, err = instance.ParseContainerType(firstPart); err == nil {
					mid = specParts[1]
				} else {
					mid = machineIdSpec
				}
			}
			if !names.IsValidMachine(mid) {
				return nil, fmt.Errorf("invalid force machine id %q", mid)
			}
			var unitCons *constraints.Value
			unitCons, err = unit.Constraints()
			if err != nil {
				return nil, err
			}

			var err error
			var m *state.Machine
			// If a container is to be used, create it.
			if containerType != "" {
				// Create the new machine marked as dirty so that
				// nothing else will grab it before we assign the unit to it.
				template := state.MachineTemplate{
					Series:            unit.Series(),
					Jobs:              []state.MachineJob{state.JobHostUnits},
					Dirty:             true,
					Constraints:       *unitCons,
					RequestedNetworks: networks,
				}
				m, err = st.AddMachineInsideMachine(template, mid, containerType)
			} else {
				m, err = st.Machine(mid)
			}
			if err != nil {
				return nil, fmt.Errorf("cannot assign unit %q to machine: %v", unit.Name(), err)
			}
			err = unit.AssignToMachine(m)

			if err != nil {
				return nil, err
			}
		} else if err := st.AssignUnit(unit, policy); err != nil {
			return nil, err
		}
		units[i] = unit
	}
	return units, nil
}
Example #28
0
func (cfg *InstanceConfig) VerifyConfig() (err error) {
	defer errors.DeferredAnnotatef(&err, "invalid machine configuration")
	if !names.IsValidMachine(cfg.MachineId) {
		return errors.New("invalid machine id")
	}
	if cfg.DataDir == "" {
		return errors.New("missing var directory")
	}
	if cfg.LogDir == "" {
		return errors.New("missing log directory")
	}
	if len(cfg.Jobs) == 0 {
		return errors.New("missing machine jobs")
	}
	if cfg.CloudInitOutputLog == "" {
		return errors.New("missing cloud-init output log path")
	}
	if cfg.Tools == nil {
		return errors.New("missing tools")
	}
	if cfg.Tools.URL == "" {
		return errors.New("missing tools URL")
	}
	if cfg.MongoInfo == nil {
		return errors.New("missing state info")
	}
	if len(cfg.MongoInfo.CACert) == 0 {
		return errors.New("missing CA certificate")
	}
	if cfg.APIInfo == nil {
		return errors.New("missing API info")
	}
	if cfg.APIInfo.EnvironTag.Id() == "" {
		return errors.New("missing environment tag")
	}
	if len(cfg.APIInfo.CACert) == 0 {
		return errors.New("missing API CA certificate")
	}
	if cfg.MachineAgentServiceName == "" {
		return errors.New("missing machine agent service name")
	}
	if cfg.Bootstrap {
		if cfg.Config == nil {
			return errors.New("missing environment configuration")
		}
		if cfg.MongoInfo.Tag != nil {
			return errors.New("entity tag must be nil when starting a state server")
		}
		if cfg.APIInfo.Tag != nil {
			return errors.New("entity tag must be nil when starting a state server")
		}
		if cfg.StateServingInfo == nil {
			return errors.New("missing state serving info")
		}
		if len(cfg.StateServingInfo.Cert) == 0 {
			return errors.New("missing state server certificate")
		}
		if len(cfg.StateServingInfo.PrivateKey) == 0 {
			return errors.New("missing state server private key")
		}
		if len(cfg.StateServingInfo.CAPrivateKey) == 0 {
			return errors.New("missing ca cert private key")
		}
		if cfg.StateServingInfo.StatePort == 0 {
			return errors.New("missing state port")
		}
		if cfg.StateServingInfo.APIPort == 0 {
			return errors.New("missing API port")
		}
		if cfg.InstanceId == "" {
			return errors.New("missing instance-id")
		}
	} else {
		if len(cfg.MongoInfo.Addrs) == 0 {
			return errors.New("missing state hosts")
		}
		if cfg.MongoInfo.Tag != names.NewMachineTag(cfg.MachineId) {
			return errors.New("entity tag must match started machine")
		}
		if len(cfg.APIInfo.Addrs) == 0 {
			return errors.New("missing API hosts")
		}
		if cfg.APIInfo.Tag != names.NewMachineTag(cfg.MachineId) {
			return errors.New("entity tag must match started machine")
		}
		if cfg.StateServingInfo != nil {
			return errors.New("state serving info unexpectedly present")
		}
	}
	if cfg.MachineNonce == "" {
		return errors.New("missing machine nonce")
	}
	return nil
}
Example #29
0
// ServiceDeploy fetches the charm from the charm store and deploys it.
// AddCharm or AddLocalCharm should be called to add the charm
// before calling ServiceDeploy, although for backward compatibility
// this is not necessary until 1.16 support is removed.
func (c *Client) ServiceDeploy(args params.ServiceDeploy) error {
	curl, err := charm.ParseURL(args.CharmUrl)
	if err != nil {
		return err
	}
	if curl.Revision < 0 {
		return fmt.Errorf("charm url must include revision")
	}

	if args.ToMachineSpec != "" && names.IsValidMachine(args.ToMachineSpec) {
		_, err = c.api.state.Machine(args.ToMachineSpec)
		if err != nil {
			return fmt.Errorf(`cannot deploy "%v" to machine %v: %v`, args.ServiceName, args.ToMachineSpec, err)
		}
	}

	// Try to find the charm URL in state first.
	ch, err := c.api.state.Charm(curl)
	if errors.IsNotFound(err) {
		// Remove this whole if block when 1.16 compatibility is dropped.
		if curl.Schema != "cs" {
			return fmt.Errorf(`charm url has unsupported schema %q`, curl.Schema)
		}
		err = c.AddCharm(params.CharmURL{args.CharmUrl})
		if err != nil {
			return err
		}
		ch, err = c.api.state.Charm(curl)
		if err != nil {
			return err
		}
	} else if err != nil {
		return err
	}

	var settings charm.Settings
	if len(args.ConfigYAML) > 0 {
		settings, err = ch.Config().ParseSettingsYAML([]byte(args.ConfigYAML), args.ServiceName)
	} else if len(args.Config) > 0 {
		// Parse config in a compatile way (see function comment).
		settings, err = parseSettingsCompatible(ch, args.Config)
	}
	if err != nil {
		return err
	}
	// Convert network tags to names for any given networks.
	requestedNetworks, err := networkTagsToNames(args.Networks)
	if err != nil {
		return err
	}

	_, err = juju.DeployService(c.api.state,
		juju.DeployServiceParams{
			ServiceName: args.ServiceName,
			// TODO(dfc) ServiceOwner should be a tag
			ServiceOwner:   c.api.auth.GetAuthTag().String(),
			Charm:          ch,
			NumUnits:       args.NumUnits,
			ConfigSettings: settings,
			Constraints:    args.Constraints,
			ToMachineSpec:  args.ToMachineSpec,
			Networks:       requestedNetworks,
		})
	return err
}