Beispiel #1
0
// Login authenticates as the entity with the given name and password.
// Subsequent requests on the state will act as that entity.  This
// method is usually called automatically by Open. The machine nonce
// should be empty unless logging in as a machine agent.
func (st *State) Login(tag, password, nonce string) error {
	err := st.loginV2(tag, password, nonce)
	if params.IsCodeNotImplemented(err) {
		err = st.loginV1(tag, password, nonce)
		if params.IsCodeNotImplemented(err) {
			// TODO (cmars): remove fallback once we can drop v0 compatibility
			return st.loginV0(tag, password, nonce)
		}
	}
	return err
}
Beispiel #2
0
// AddMetricsBatches makes an api call to the uniter requesting it to store metrics batches in state.
func (u *Unit) AddMetricBatches(batches []params.MetricBatch) (map[string]error, error) {
	p := params.MetricBatchParams{
		Batches: make([]params.MetricBatchParam, len(batches)),
	}

	batchResults := make(map[string]error, len(batches))

	for i, batch := range batches {
		p.Batches[i].Tag = u.tag.String()
		p.Batches[i].Batch = batch

		batchResults[batch.UUID] = nil
	}
	results := new(params.ErrorResults)
	err := u.st.facade.FacadeCall("AddMetricBatches", p, results)
	if params.IsCodeNotImplemented(err) {
		for _, batch := range batches {
			err = u.AddMetrics(batch.Metrics)
			if err != nil {
				batchResults[batch.UUID] = errors.Annotate(err, "failed to send metric batch")
			}
		}
		return batchResults, nil
	} else if err != nil {
		return nil, errors.Annotate(err, "failed to send metric batches")
	}
	for i, result := range results.Results {
		batchResults[batches[i].UUID] = result.Error
	}
	return batchResults, nil
}
func containerManagerConfig(
	containerType instance.ContainerType,
	provisioner *apiprovisioner.State,
	agentConfig agent.Config,
) (container.ManagerConfig, error) {
	// Ask the provisioner for the container manager configuration.
	managerConfigResult, err := provisioner.ContainerManagerConfig(
		params.ContainerManagerConfigParams{Type: containerType},
	)
	if params.IsCodeNotImplemented(err) {
		// We currently don't support upgrading;
		// revert to the old configuration.
		managerConfigResult.ManagerConfig = container.ManagerConfig{container.ConfigName: container.DefaultNamespace}
	}
	if err != nil {
		return nil, err
	}
	// If a namespace is specified, that should instead be used as the config name.
	if namespace := agentConfig.Value(agent.Namespace); namespace != "" {
		managerConfigResult.ManagerConfig[container.ConfigName] = namespace
	}
	managerConfig := container.ManagerConfig(managerConfigResult.ManagerConfig)

	return managerConfig, nil
}
Beispiel #4
0
// runRestore will implement the actual calls to the different Client parts
// of restore.
func (c *restoreCommand) runRestore(ctx *cmd.Context) error {
	client, closer, err := c.newClient()
	if err != nil {
		return errors.Trace(err)
	}
	defer closer()
	var target string
	var rErr error
	if c.filename != "" {
		target = c.filename
		archive, meta, err := getArchive(c.filename)
		if err != nil {
			return errors.Trace(err)
		}
		defer archive.Close()

		rErr = client.RestoreReader(archive, meta, c.newClient)
	} else {
		target = c.backupId
		rErr = client.Restore(c.backupId, c.newClient)
	}
	if params.IsCodeNotImplemented(rErr) {
		return errors.Errorf(restoreAPIIncompatibility)
	}
	if rErr != nil {
		return errors.Trace(rErr)
	}

	fmt.Fprintf(ctx.Stdout, "restore from %q completed\n", target)
	return nil
}
Beispiel #5
0
// SetStatus sets the status of the service if the passed unitName,
// corresponding to the calling unit, is of the leader.
func (s *Service) SetStatus(unitName string, status params.Status, info string, data map[string]interface{}) error {
	//TODO(perrito666) bump api version for this?
	if s.st.facade.BestAPIVersion() < 2 {
		return errors.NotImplementedf("SetStatus")
	}
	tag := names.NewUnitTag(unitName)
	var result params.ErrorResults
	args := params.SetStatus{
		Entities: []params.EntityStatusArgs{
			{
				Tag:    tag.String(),
				Status: status,
				Info:   info,
				Data:   data,
			},
		},
	}
	err := s.st.facade.FacadeCall("SetServiceStatus", args, &result)
	if err != nil {
		if params.IsCodeNotImplemented(err) {
			return errors.NotImplementedf("SetServiceStatus")
		}
		return errors.Trace(err)
	}
	return result.OneError()
}
Beispiel #6
0
func (c *DestroyEnvironmentCommand) Run(ctx *cmd.Context) (result error) {
	store, err := configstore.Default()
	if err != nil {
		return fmt.Errorf("cannot open environment info storage: %v", err)
	}
	environ, err := environs.NewFromName(c.envName, store)
	if err != nil {
		if environs.IsEmptyConfig(err) {
			// Delete the .jenv file and call it done.
			ctx.Infof("removing empty environment file")
			return environs.DestroyInfo(c.envName, store)
		}
		return err
	}
	if !c.assumeYes {
		fmt.Fprintf(ctx.Stdout, destroyEnvMsg, c.envName, environ.Config().Type())

		scanner := bufio.NewScanner(ctx.Stdin)
		scanner.Scan()
		err := scanner.Err()
		if err != nil && err != io.EOF {
			return fmt.Errorf("Environment destruction aborted: %s", err)
		}
		answer := strings.ToLower(scanner.Text())
		if answer != "y" && answer != "yes" {
			return errors.New("environment destruction aborted")
		}
	}
	// If --force is supplied, then don't attempt to use the API.
	// This is necessary to destroy broken environments, where the
	// API server is inaccessible or faulty.
	if !c.force {
		defer func() {
			if result == nil {
				return
			}
			logger.Errorf(`failed to destroy environment %q
        
If the environment is unusable, then you may run

    juju destroy-environment --force

to forcefully destroy the environment. Upon doing so, review
your environment provider console for any resources that need
to be cleaned up.

`, c.envName)
		}()
		apiclient, err := juju.NewAPIClientFromName(c.envName)
		if err != nil {
			return fmt.Errorf("cannot connect to API: %v", err)
		}
		defer apiclient.Close()
		err = apiclient.DestroyEnvironment()
		if err != nil && !params.IsCodeNotImplemented(err) {
			return fmt.Errorf("destroying environment: %v", err)
		}
	}
	return environs.Destroy(environ, store)
}
Beispiel #7
0
// getJoinedRelations finds out what relations the unit is *really* part of,
// working around the fact that pre-1.19 (1.18.1?) unit agents don't write a
// state dir for a relation until a remote unit joins.
func (u *Uniter) getJoinedRelations() (map[int]*uniter.Relation, error) {
	var joinedRelationTags []string
	for {
		var err error
		joinedRelationTags, err = u.unit.JoinedRelations()
		if err == nil {
			break
		}
		if params.IsCodeNotImplemented(err) {
			logger.Infof("waiting for state server to be upgraded")
			select {
			case <-u.tomb.Dying():
				return nil, tomb.ErrDying
			case <-time.After(15 * time.Second):
				continue
			}
		}
		return nil, err
	}
	joinedRelations := make(map[int]*uniter.Relation)
	for _, tag := range joinedRelationTags {
		relation, err := u.st.Relation(tag)
		if err != nil {
			return nil, err
		}
		joinedRelations[relation.Id()] = relation
	}
	return joinedRelations, nil
}
Beispiel #8
0
func (task *provisionerTask) startMachine(
	machine *apiprovisioner.Machine,
	provisioningInfo *params.ProvisioningInfo,
	startInstanceParams environs.StartInstanceParams,
) error {

	inst, metadata, networkInfo, err := task.broker.StartInstance(startInstanceParams)
	if err != nil {
		// Set the state to error, so the machine will be skipped next
		// time until the error is resolved, but don't return an
		// error; just keep going with the other machines.
		return task.setErrorStatus("cannot start instance for machine %q: %v", machine, err)
	}

	nonce := startInstanceParams.MachineConfig.MachineNonce
	networks, ifaces := task.prepareNetworkAndInterfaces(networkInfo)

	err = machine.SetInstanceInfo(inst.Id(), nonce, metadata, networks, ifaces)
	if err != nil && params.IsCodeNotImplemented(err) {
		return fmt.Errorf("cannot provision instance %v for machine %q with networks: not implemented", inst.Id(), machine)
	} else if err == nil {
		logger.Infof("started machine %s as instance %s with hardware %q, networks %v, interfaces %v", machine, inst.Id(), metadata, networks, ifaces)
		return nil
	}
	// We need to stop the instance right away here, set error status and go on.
	task.setErrorStatus("cannot register instance for machine %v: %v", machine, err)
	if err := task.broker.StopInstances(inst.Id()); err != nil {
		// We cannot even stop the instance, log the error and quit.
		return errors.Annotatef(err, "cannot stop instance %q for machine %v", inst.Id(), machine)
	}
	return nil
}
Beispiel #9
0
// getControllerEnviron gets the bootstrap information required to destroy the
// environment by first checking the config store, then querying the API if
// the information is not in the store.
func (c *destroyCommandBase) getControllerEnviron(info configstore.EnvironInfo, sysAPI destroyControllerAPI) (_ environs.Environ, err error) {
	bootstrapCfg := info.BootstrapConfig()
	if bootstrapCfg == nil {
		if sysAPI == nil {
			return nil, errors.New("unable to get bootstrap information from API")
		}
		bootstrapCfg, err = sysAPI.EnvironmentConfig()
		if params.IsCodeNotImplemented(err) {
			// Fallback to the client API. Better to encapsulate the logic for
			// old servers than worry about connecting twice.
			client, err := c.getClientAPI()
			if err != nil {
				return nil, errors.Trace(err)
			}
			defer client.Close()
			bootstrapCfg, err = client.EnvironmentGet()
			if err != nil {
				return nil, errors.Trace(err)
			}
		} else if err != nil {
			return nil, errors.Trace(err)
		}
	}

	cfg, err := config.New(config.NoDefaults, bootstrapCfg)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return environs.New(cfg)
}
Beispiel #10
0
func (task *provisionerTask) startMachine(
	machine *apiprovisioner.Machine,
	provisioningInfo *params.ProvisioningInfo,
	startInstanceParams environs.StartInstanceParams,
) error {

	result, err := task.broker.StartInstance(startInstanceParams)
	if err != nil {
		// If this is a retryable error, we retry once
		if instance.IsRetryableCreationError(errors.Cause(err)) {
			logger.Infof("retryable error received on start instance - retrying instance creation")
			result, err = task.broker.StartInstance(startInstanceParams)
			if err != nil {
				return task.setErrorStatus("cannot start instance for machine after a retry %q: %v", machine, err)
			}
		} else {
			// Set the state to error, so the machine will be skipped next
			// time until the error is resolved, but don't return an
			// error; just keep going with the other machines.
			return task.setErrorStatus("cannot start instance for machine %q: %v", machine, err)
		}
	}

	inst := result.Instance
	hardware := result.Hardware
	nonce := startInstanceParams.InstanceConfig.MachineNonce
	networks, ifaces, err := task.prepareNetworkAndInterfaces(result.NetworkInfo)
	if err != nil {
		return task.setErrorStatus("cannot prepare network for machine %q: %v", machine, err)
	}
	volumes := volumesToApiserver(result.Volumes)
	volumeAttachments := volumeAttachmentsToApiserver(result.VolumeAttachments)

	// TODO(dimitern) In a newer Provisioner API version, change
	// SetInstanceInfo or add a new method that takes and saves in
	// state all the information available on a network.InterfaceInfo
	// for each interface, so we can later manage interfaces
	// dynamically at run-time.
	err = machine.SetInstanceInfo(inst.Id(), nonce, hardware, networks, ifaces, volumes, volumeAttachments)
	if err != nil && params.IsCodeNotImplemented(err) {
		return fmt.Errorf("cannot provision instance %v for machine %q with networks: not implemented", inst.Id(), machine)
	} else if err == nil {
		logger.Infof(
			"started machine %s as instance %s with hardware %q, networks %v, interfaces %v, volumes %v, volume attachments %v, subnets to zones %v",
			machine, inst.Id(), hardware,
			networks, ifaces,
			volumes, volumeAttachments,
			startInstanceParams.SubnetsToZones,
		)
		return nil
	}
	// We need to stop the instance right away here, set error status and go on.
	task.setErrorStatus("cannot register instance for machine %v: %v", machine, err)
	if err := task.broker.StopInstances(inst.Id()); err != nil {
		// We cannot even stop the instance, log the error and quit.
		return errors.Annotatef(err, "cannot stop instance %q for machine %v", inst.Id(), machine)
	}
	return nil
}
Beispiel #11
0
// processDestroyError determines how to format error message based on its code.
// Note that CodeNotImplemented errors have not be propogated in previous implementation.
// This behaviour was preserved.
func processDestroyError(err error) error {
	if err == nil || params.IsCodeNotImplemented(err) {
		return nil
	}
	if params.IsCodeOperationBlocked(err) {
		return err
	}
	return errors.Annotate(err, "destroying environment")
}
Beispiel #12
0
func machineLoop(context machineContext, m machine, changed <-chan struct{}) error {
	// Use a short poll interval when initially waiting for
	// a machine's address and machine agent to start, and a long one when it already
	// has an address and the machine agent is started.
	pollInterval := ShortPoll
	pollInstance := true
	for {
		if pollInstance {
			instInfo, err := pollInstanceInfo(context, m)
			if err != nil && !params.IsCodeNotProvisioned(err) {
				// If the provider doesn't implement Addresses/Status now,
				// it never will until we're upgraded, so don't bother
				// asking any more. We could use less resources
				// by taking down the entire worker, but this is easier for now
				// (and hopefully the local provider will implement
				// Addresses/Status in the not-too-distant future),
				// so we won't need to worry about this case at all.
				if params.IsCodeNotImplemented(err) {
					pollInterval = 365 * 24 * time.Hour
				} else {
					return err
				}
			}
			machineStatus := params.StatusPending
			if err == nil {
				if statusInfo, err := m.Status(); err != nil {
					logger.Warningf("cannot get current machine status for machine %v: %v", m.Id(), err)
				} else {
					machineStatus = statusInfo.Status
				}
			}
			if len(instInfo.addresses) > 0 && instInfo.status != "" && machineStatus == params.StatusStarted {
				// We've got at least one address and a status and instance is started, so poll infrequently.
				pollInterval = LongPoll
			} else if pollInterval < LongPoll {
				// We have no addresses or not started - poll increasingly rarely
				// until we do.
				pollInterval = time.Duration(float64(pollInterval) * ShortPollBackoff)
			}
			pollInstance = false
		}
		select {
		case <-time.After(pollInterval):
			pollInstance = true
		case <-context.dying():
			return nil
		case <-changed:
			if err := m.Refresh(); err != nil {
				return err
			}
			if m.Life() == params.Dead {
				return nil
			}
		}
	}
}
Beispiel #13
0
// pollInstanceInfo checks the current provider addresses and status
// for the given machine's instance, and sets them on the machine if they've changed.
func pollInstanceInfo(context machineContext, m machine) (instInfo instanceInfo, err error) {
	instInfo = instanceInfo{}
	instId, err := m.InstanceId()
	// We can't ask the machine for its addresses if it isn't provisioned yet.
	if params.IsCodeNotProvisioned(err) {
		return instanceInfo{}, err
	}
	if err != nil {
		return instanceInfo{}, errors.Annotate(err, "cannot get machine's instance id")
	}
	instInfo, err = context.instanceInfo(instId)
	if err != nil {
		// TODO (anastasiamac 2016-02-01) This does not look like it needs to be removed now.
		if params.IsCodeNotImplemented(err) {
			return instanceInfo{}, err
		}
		logger.Warningf("cannot get instance info for instance %q: %v", instId, err)
		return instInfo, nil
	}
	if instStat, err := m.InstanceStatus(); err != nil {
		// This should never occur since the machine is provisioned.
		// But just in case, we reset polled status so we try again next time.
		logger.Warningf("cannot get current instance status for machine %v: %v", m.Id(), err)
		instInfo.status = instance.InstanceStatus{status.Unknown, ""}
	} else {
		// TODO(perrito666) add status validation.
		currentInstStatus := instance.InstanceStatus{
			Status:  status.Status(instStat.Status),
			Message: instStat.Info,
		}
		if instInfo.status != currentInstStatus {
			logger.Infof("machine %q instance status changed from %q to %q", m.Id(), currentInstStatus, instInfo.status)
			if err = m.SetInstanceStatus(instInfo.status.Status, instInfo.status.Message, nil); err != nil {
				logger.Errorf("cannot set instance status on %q: %v", m, err)
				return instanceInfo{}, err
			}
		}

	}
	if m.Life() != params.Dead {
		providerAddresses, err := m.ProviderAddresses()
		if err != nil {
			return instanceInfo{}, err
		}
		if !addressesEqual(providerAddresses, instInfo.addresses) {
			logger.Infof("machine %q has new addresses: %v", m.Id(), instInfo.addresses)
			if err := m.SetProviderAddresses(instInfo.addresses...); err != nil {
				logger.Errorf("cannot set addresses on %q: %v", m, err)
				return instanceInfo{}, err
			}
		}
	}
	return instInfo, nil
}
Beispiel #14
0
// Run implements Command.Run
func (c *destroyCommand) Run(ctx *cmd.Context) error {
	store, err := configstore.Default()
	if err != nil {
		return errors.Annotate(err, "cannot open system info storage")
	}

	cfgInfo, err := store.ReadInfo(c.systemName)
	if err != nil {
		return errors.Annotate(err, "cannot read system info")
	}

	// Verify that we're destroying a system
	apiEndpoint := cfgInfo.APIEndpoint()
	if apiEndpoint.ServerUUID != "" && apiEndpoint.EnvironUUID != apiEndpoint.ServerUUID {
		return errors.Errorf("%q is not a system; use juju environment destroy to destroy it", c.systemName)
	}

	if !c.assumeYes {
		if err = confirmDestruction(ctx, c.systemName); err != nil {
			return err
		}
	}

	// Attempt to connect to the API.  If we can't, fail the destroy.  Users will
	// need to use the system kill command if we can't connect.
	api, err := c.getSystemAPI()
	if err != nil {
		return c.ensureUserFriendlyErrorLog(errors.Annotate(err, "cannot connect to API"), ctx, nil)
	}
	defer api.Close()

	// Obtain bootstrap / system environ information
	systemEnviron, err := c.getSystemEnviron(cfgInfo, api)
	if err != nil {
		return errors.Annotate(err, "cannot obtain bootstrap information")
	}

	// Attempt to destroy the system.
	err = api.DestroySystem(c.destroyEnvs, false)
	if params.IsCodeNotImplemented(err) {
		// Fall back to using the client endpoint to destroy the system,
		// sending the info we were already able to collect.
		return c.destroySystemViaClient(ctx, cfgInfo, systemEnviron, store)
	}
	if err != nil {
		return c.ensureUserFriendlyErrorLog(errors.Annotate(err, "cannot destroy system"), ctx, api)
	}

	return environs.Destroy(systemEnviron, store)
}
Beispiel #15
0
// UnitStatusHistory retrieves the last <size> results of <kind:combined|agent|workload> status
// for <unitName> unit
func (c *Client) UnitStatusHistory(kind params.HistoryKind, unitName string, size int) (*UnitStatusHistory, error) {
	var results UnitStatusHistory
	args := params.StatusHistory{
		Kind: kind,
		Size: size,
		Name: unitName,
	}
	err := c.facade.FacadeCall("UnitStatusHistory", args, &results)
	if err != nil {
		if params.IsCodeNotImplemented(err) {
			return &UnitStatusHistory{}, errors.NotImplementedf("UnitStatusHistory")
		}
		return &UnitStatusHistory{}, errors.Trace(err)
	}
	return &results, nil
}
Beispiel #16
0
// Environment returns the environment entity.
func (st *State) Environment() (*Environment, error) {
	var result params.EnvironmentResult
	err := st.facade.FacadeCall("CurrentEnvironment", nil, &result)
	if params.IsCodeNotImplemented(err) {
		// Fall back to using the 1.16 API.
		return st.environment1dot16()
	}
	if err != nil {
		return nil, err
	}
	if err := result.Error; err != nil {
		return nil, err
	}
	return &Environment{
		name: result.Name,
		uuid: result.UUID,
	}, nil
}
Beispiel #17
0
// ServiceDeploy obtains the charm, either locally or from
// the charm store, and deploys it. It allows the specification of
// requested networks that must be present on the machines where the
// service is deployed. Another way to specify networks to include/exclude
// is using constraints. Placement directives, if provided, specify the
// machine on which the charm is deployed.
func (c *Client) ServiceDeploy(
	charmURL string,
	serviceName string,
	series string,
	numUnits int,
	configYAML string,
	cons constraints.Value,
	toMachineSpec string,
	placement []*instance.Placement,
	networks []string,
	storage map[string]storage.Constraints,
) error {
	args := params.ServicesDeploy{
		Services: []params.ServiceDeploy{{
			ServiceName:   serviceName,
			Series:        series,
			CharmUrl:      charmURL,
			NumUnits:      numUnits,
			ConfigYAML:    configYAML,
			Constraints:   cons,
			ToMachineSpec: toMachineSpec,
			Placement:     placement,
			Networks:      networks,
			Storage:       storage,
		}},
	}
	var results params.ErrorResults
	var err error
	if len(placement) > 0 {
		err = c.FacadeCall("ServicesDeployWithPlacement", args, &results)
		if err != nil {
			if params.IsCodeNotImplemented(err) {
				return errors.Errorf("unsupported --to parameter %q", toMachineSpec)
			}
			return err
		}
	} else {
		err = c.FacadeCall("ServicesDeploy", args, &results)
	}
	if err != nil {
		return err
	}
	return results.OneError()
}
Beispiel #18
0
// pollInstanceInfo checks the current provider addresses and status
// for the given machine's instance, and sets them on the machine if they've changed.
func pollInstanceInfo(context machineContext, m machine) (instInfo instanceInfo, err error) {
	instInfo = instanceInfo{}
	instId, err := m.InstanceId()
	// We can't ask the machine for its addresses if it isn't provisioned yet.
	if params.IsCodeNotProvisioned(err) {
		return instInfo, err
	}
	if err != nil {
		return instInfo, fmt.Errorf("cannot get machine's instance id: %v", err)
	}
	instInfo, err = context.instanceInfo(instId)
	if err != nil {
		if params.IsCodeNotImplemented(err) {
			return instInfo, err
		}
		logger.Warningf("cannot get instance info for instance %q: %v", instId, err)
		return instInfo, nil
	}
	currentInstStatus, err := m.InstanceStatus()
	if err != nil {
		// This should never occur since the machine is provisioned.
		// But just in case, we reset polled status so we try again next time.
		logger.Warningf("cannot get current instance status for machine %v: %v", m.Id(), err)
		instInfo.status = ""
	} else {
		if instInfo.status != currentInstStatus {
			logger.Infof("machine %q instance status changed from %q to %q", m.Id(), currentInstStatus, instInfo.status)
			if err = m.SetInstanceStatus(instInfo.status); err != nil {
				logger.Errorf("cannot set instance status on %q: %v", m, err)
			}
		}
	}
	providerAddresses, err := m.ProviderAddresses()
	if err != nil {
		return instInfo, err
	}
	if !addressesEqual(providerAddresses, instInfo.addresses) {
		logger.Infof("machine %q has new addresses: %v", m.Id(), instInfo.addresses)
		if err = m.SetProviderAddresses(instInfo.addresses...); err != nil {
			logger.Errorf("cannot set addresses on %q: %v", m, err)
		}
	}
	return instInfo, err
}
Beispiel #19
0
// MachineNetworkConfig returns information about network interfaces to
// setup only for a single machine.
func (st *state) MachineNetworkConfig(tag names.MachineTag) ([]network.InterfaceInfo, error) {
	args := params.Entities{
		Entities: []params.Entity{{Tag: tag.String()}},
	}
	var results params.MachineNetworkConfigResults
	err := st.facade.FacadeCall("MachineNetworkConfig", args, &results)
	if err != nil {
		if params.IsCodeNotImplemented(err) {
			// Fallback to former name.
			err = st.facade.FacadeCall("MachineNetworkInfo", args, &results)
		}
		if err != nil {
			// TODO: Not directly tested.
			return nil, err
		}
	}
	if len(results.Results) != 1 {
		// TODO: Not directly tested
		err = errors.Errorf("expected one result, got %d", len(results.Results))
		return nil, err
	}
	result := results.Results[0]
	if result.Error != nil {
		return nil, result.Error
	}
	interfaceInfo := make([]network.InterfaceInfo, len(result.Config))
	for i, ifaceInfo := range result.Config {
		interfaceInfo[i].DeviceIndex = ifaceInfo.DeviceIndex
		interfaceInfo[i].MACAddress = ifaceInfo.MACAddress
		interfaceInfo[i].CIDR = ifaceInfo.CIDR
		interfaceInfo[i].NetworkName = ifaceInfo.NetworkName
		interfaceInfo[i].ProviderId = network.Id(ifaceInfo.ProviderId)
		interfaceInfo[i].VLANTag = ifaceInfo.VLANTag
		interfaceInfo[i].InterfaceName = ifaceInfo.InterfaceName
		interfaceInfo[i].Disabled = ifaceInfo.Disabled
		// TODO(dimitern) Once we store all the information from
		// network.InterfaceInfo in state, change this as needed to
		// return it.
	}

	return interfaceInfo, nil
}
Beispiel #20
0
// Run connects to the environment specified on the command line
// and calls AddServiceUnits for the given service.
func (c *addUnitCommand) Run(_ *cmd.Context) error {
	apiclient, err := c.getAPI()
	if err != nil {
		return err
	}
	defer apiclient.Close()

	conf, err := GetClientConfig(apiclient)
	if err != nil {
		return err
	}

	if err := c.CheckProvider(conf); err != nil {
		return err
	}

	for i, p := range c.Placement {
		if p.Scope == "env-uuid" {
			p.Scope = apiclient.EnvironmentUUID()
		}
		c.Placement[i] = p
	}
	if len(c.Placement) > 0 {
		_, err = apiclient.AddServiceUnitsWithPlacement(c.ServiceName, c.NumUnits, c.Placement)
		if err == nil {
			return nil
		}
		if !params.IsCodeNotImplemented(err) {
			return block.ProcessBlockedError(err, block.BlockChange)
		}
	}
	if c.PlacementSpec != "" && !IsMachineOrNewContainer(c.PlacementSpec) {
		return errors.Errorf("unsupported --to parameter %q", c.PlacementSpec)
	}
	if c.PlacementSpec != "" && c.NumUnits > 1 {
		return errors.New("this version of Juju does not support --num-units > 1 with --to")
	}
	_, err = apiclient.AddServiceUnits(c.ServiceName, c.NumUnits, c.PlacementSpec)
	return block.ProcessBlockedError(err, block.BlockChange)
}
Beispiel #21
0
// RunPre obtains authorization to deploy this charm. The authorization, if received is not
// sent to the controller, rather it is kept as an attribute on RegisterMeteredCharm.
func (r *RegisterMeteredCharm) RunPre(state api.Connection, client *http.Client, deployInfo DeploymentInfo) error {
	charmsClient := charms.NewClient(state)
	defer charmsClient.Close()
	metered, err := charmsClient.IsMetered(deployInfo.CharmURL.String())
	if params.IsCodeNotImplemented(err) {
		// The state server is too old to support metering.  Warn
		// the user, but don't return an error.
		logger.Tracef("current state server version does not support charm metering")
		return nil
	} else if err != nil {
		return err
	}
	if !metered {
		return nil
	}

	bakeryClient := httpbakery.Client{Client: client, VisitWebPage: httpbakery.OpenWebBrowser}

	if r.Plan == "" {
		r.Plan, err = r.getDefaultPlan(client, deployInfo.CharmURL.String())
		if err != nil {
			if isNoDefaultPlanError(err) {
				options, err1 := r.getCharmPlans(client, deployInfo.CharmURL.String())
				if err1 != nil {
					return err1
				}
				charmUrl := deployInfo.CharmURL.String()
				return errors.Errorf(`%v has no default plan. Try "juju deploy --plan <plan-name> with one of %v"`, charmUrl, strings.Join(options, ", "))
			}
			return err
		}
	}

	r.credentials, err = r.registerMetrics(deployInfo.EnvUUID, deployInfo.CharmURL.String(), deployInfo.ServiceName, &bakeryClient)
	if err != nil {
		logger.Infof("failed to obtain plan authorization: %v", err)
		return err
	}
	return nil
}
Beispiel #22
0
// RunPre is part of the DeployStep interface.
func (a *AllocateBudget) RunPre(state api.Connection, client *http.Client, ctx *cmd.Context, deployInfo DeploymentInfo) error {
	if deployInfo.CharmURL.Schema == "local" {
		return nil
	}
	charmsClient := charms.NewClient(state)
	metered, err := charmsClient.IsMetered(deployInfo.CharmURL.String())
	if params.IsCodeNotImplemented(err) {
		// The state server is too old to support metering.  Warn
		// the user, but don't return an error.
		logger.Tracef("current state server version does not support charm metering")
		return nil
	} else if err != nil {
		return errors.Annotate(err, "could not determine charm type")
	}
	if !metered {
		return nil
	}

	allocBudget, allocLimit, err := parseBudgetWithLimit(a.AllocationSpec)
	if err != nil {
		return errors.Trace(err)
	}
	a.APIClient, err = getApiClient(client)
	if err != nil {
		return errors.Annotate(err, "could not create API client")
	}
	resp, err := a.APIClient.CreateAllocation(allocBudget, allocLimit, deployInfo.ModelUUID, []string{deployInfo.ServiceName})
	if err != nil {
		if wireformat.IsNotAvail(err) {
			fmt.Fprintf(ctx.Stdout, "WARNING: Allocation not created - %s.\n", err.Error())
			return nil
		}
		return errors.Annotate(err, "could not create budget allocation")
	}
	a.allocated = true
	fmt.Fprintf(ctx.Stdout, "%s\n", resp)
	return nil
}
Beispiel #23
0
// ServiceStatus returns the status of the service if the passed unitName,
// corresponding to the calling unit, is of the leader.
func (s *Service) Status(unitName string) (params.ServiceStatusResult, error) {
	tag := names.NewUnitTag(unitName)
	var results params.ServiceStatusResults
	args := params.Entities{
		Entities: []params.Entity{
			{
				Tag: tag.String(),
			},
		},
	}
	err := s.st.facade.FacadeCall("ServiceStatus", args, &results)
	if err != nil {
		if params.IsCodeNotImplemented(err) {
			return params.ServiceStatusResult{}, errors.NotImplementedf("ServiceStatus")
		}
		return params.ServiceStatusResult{}, errors.Trace(err)
	}
	result := results.Results[0]
	if result.Error != nil {
		return params.ServiceStatusResult{}, result.Error
	}
	return result, nil
}
Beispiel #24
0
// UnitStatus gets the status details of the unit.
func (u *Unit) UnitStatus() (params.StatusResult, error) {
	var results params.StatusResults
	args := params.Entities{
		Entities: []params.Entity{
			{Tag: u.tag.String()},
		},
	}
	err := u.st.facade.FacadeCall("UnitStatus", args, &results)
	if err != nil {
		if params.IsCodeNotImplemented(err) {
			return params.StatusResult{}, errors.NotImplementedf("UnitStatus")
		}
		return params.StatusResult{}, errors.Trace(err)
	}
	if len(results.Results) != 1 {
		panic(errors.Errorf("expected 1 result, got %d", len(results.Results)))
	}
	result := results.Results[0]
	if result.Error != nil {
		return params.StatusResult{}, result.Error
	}
	return result, nil
}
Beispiel #25
0
// RunPost sends credentials obtained during the call to RunPre to the controller.
func (r *RegisterMeteredCharm) RunPost(state api.Connection, client *http.Client, deployInfo DeploymentInfo) error {
	if r.credentials == nil {
		return nil
	}
	api, cerr := getMetricCredentialsAPI(state)
	if cerr != nil {
		logger.Infof("failed to get the metrics credentials setter: %v", cerr)
		return cerr
	}
	defer api.Close()

	err := api.SetMetricCredentials(deployInfo.ServiceName, r.credentials)
	if params.IsCodeNotImplemented(err) {
		// The state server is too old to support metering.  Warn
		// the user, but don't return an error.
		logger.Warningf("current state server version does not support charm metering")
		return nil
	} else if err != nil {
		logger.Infof("failed to set metric credentials: %v", err)
		return err
	}

	return nil
}
Beispiel #26
0
// ProvisioningInfo is new in 1.20; wait for the API server to be
// upgraded so we don't spew errors on upgrade.
func (task *provisionerTask) blockUntilProvisioned(
	provision func() (*params.ProvisioningInfo, error),
) (*params.ProvisioningInfo, error) {

	var pInfo *params.ProvisioningInfo
	var err error
	for {
		if pInfo, err = provision(); err == nil {
			break
		}
		if params.IsCodeNotImplemented(err) {
			logger.Infof("waiting for state server to be upgraded")
			select {
			case <-task.tomb.Dying():
				return nil, tomb.ErrDying
			case <-time.After(15 * time.Second):
				continue
			}
		}
		return nil, err
	}

	return pInfo, nil
}
Beispiel #27
0
func (c *DeployCommand) Run(ctx *cmd.Context) error {
	client, err := c.NewAPIClient()
	if err != nil {
		return err
	}
	defer client.Close()

	conf, err := service.GetClientConfig(client)
	if err != nil {
		return err
	}

	if err := c.CheckProvider(conf); err != nil {
		return err
	}

	csClient, err := newCharmStoreClient()
	if err != nil {
		return errors.Trace(err)
	}
	defer csClient.jar.Save()
	curl, repo, err := resolveCharmURL(c.CharmName, csClient.params, ctx.AbsPath(c.RepoPath), conf)
	if err != nil {
		return errors.Trace(err)
	}

	curl, err = addCharmViaAPI(client, ctx, curl, repo, csClient)
	if err != nil {
		return block.ProcessBlockedError(err, block.BlockChange)
	}

	if c.BumpRevision {
		ctx.Infof("--upgrade (or -u) is deprecated and ignored; charms are always deployed with a unique revision.")
	}

	charmInfo, err := client.CharmInfo(curl.String())
	if err != nil {
		return err
	}

	numUnits := c.NumUnits
	if charmInfo.Meta.Subordinate {
		if !constraints.IsEmpty(&c.Constraints) {
			return errors.New("cannot use --constraints with subordinate service")
		}
		if numUnits == 1 && c.PlacementSpec == "" {
			numUnits = 0
		} else {
			return errors.New("cannot use --num-units or --to with subordinate service")
		}
	}
	serviceName := c.ServiceName
	if serviceName == "" {
		serviceName = charmInfo.Meta.Name
	}

	var configYAML []byte
	if c.Config.Path != "" {
		configYAML, err = c.Config.Read(ctx)
		if err != nil {
			return err
		}
	}

	// If storage or placement is specified, we attempt to use a new API on the service facade.
	if len(c.Storage) > 0 || len(c.Placement) > 0 {
		notSupported := errors.New("cannot deploy charms with storage or placement: not supported by the API server")
		serviceClient, err := c.newServiceAPIClient()
		if err != nil {
			return notSupported
		}
		defer serviceClient.Close()
		for i, p := range c.Placement {
			if p.Scope == "env-uuid" {
				p.Scope = serviceClient.EnvironmentUUID()
			}
			c.Placement[i] = p
		}
		err = serviceClient.ServiceDeploy(
			curl.String(),
			serviceName,
			numUnits,
			string(configYAML),
			c.Constraints,
			c.PlacementSpec,
			c.Placement,
			[]string{},
			c.Storage,
		)
		if params.IsCodeNotImplemented(err) {
			return notSupported
		}
		return block.ProcessBlockedError(err, block.BlockChange)
	}

	if len(c.Networks) > 0 {
		ctx.Infof("use of --networks is deprecated and is ignored. Please use spaces to manage placement within networks")
	}

	err = client.ServiceDeploy(
		curl.String(),
		serviceName,
		numUnits,
		string(configYAML),
		c.Constraints,
		c.PlacementSpec)

	if err != nil {
		return block.ProcessBlockedError(err, block.BlockChange)
	}

	state, err := c.NewAPIRoot()
	if err != nil {
		return err
	}
	err = registerMeteredCharm(c.RegisterURL, state, csClient.jar, curl.String(), serviceName, client.EnvironmentUUID())
	if params.IsCodeNotImplemented(err) {
		// The state server is too old to support metering.  Warn
		// the user, but don't return an error.
		logger.Warningf("current state server version does not support charm metering")
		return nil
	}

	return block.ProcessBlockedError(err, block.BlockChange)
}
Beispiel #28
0
func (c *DeployCommand) Run(ctx *cmd.Context) error {
	client, err := c.NewAPIClient()
	if err != nil {
		return err
	}
	defer client.Close()

	conf, err := service.GetClientConfig(client)
	if err != nil {
		return err
	}

	if err := c.CheckProvider(conf); err != nil {
		return err
	}

	csClient, err := newCharmStoreClient()
	if err != nil {
		return errors.Trace(err)
	}
	defer csClient.jar.Save()
	curl, repo, err := resolveCharmURL(c.CharmName, csClient.params, ctx.AbsPath(c.RepoPath), conf)
	if err != nil {
		return errors.Trace(err)
	}

	curl, err = addCharmViaAPI(client, ctx, curl, repo, csClient)
	if err != nil {
		return block.ProcessBlockedError(err, block.BlockChange)
	}

	if c.BumpRevision {
		ctx.Infof("--upgrade (or -u) is deprecated and ignored; charms are always deployed with a unique revision.")
	}

	requestedNetworks, err := networkNamesToTags(parseNetworks(c.Networks))
	if err != nil {
		return err
	}
	// We need to ensure network names are valid below, but we don't need them here.
	_, err = networkNamesToTags(c.Constraints.IncludeNetworks())
	if err != nil {
		return err
	}
	_, err = networkNamesToTags(c.Constraints.ExcludeNetworks())
	if err != nil {
		return err
	}
	haveNetworks := len(requestedNetworks) > 0 || c.Constraints.HaveNetworks()

	charmInfo, err := client.CharmInfo(curl.String())
	if err != nil {
		return err
	}

	numUnits := c.NumUnits
	if charmInfo.Meta.Subordinate {
		if !constraints.IsEmpty(&c.Constraints) {
			return errors.New("cannot use --constraints with subordinate service")
		}
		if numUnits == 1 && c.ToMachineSpec == "" {
			numUnits = 0
		} else {
			return errors.New("cannot use --num-units or --to with subordinate service")
		}
	}
	serviceName := c.ServiceName
	if serviceName == "" {
		serviceName = charmInfo.Meta.Name
	}

	var configYAML []byte
	if c.Config.Path != "" {
		configYAML, err = c.Config.Read(ctx)
		if err != nil {
			return err
		}
	}

	// If storage is specified, we attempt to use a new API on the service facade.
	if len(c.Storage) > 0 {
		notSupported := errors.New("cannot deploy charms with storage: not supported by the API server")
		serviceClient, err := c.newServiceAPIClient()
		if err != nil {
			return notSupported
		}
		defer serviceClient.Close()
		err = serviceClient.ServiceDeploy(
			curl.String(),
			serviceName,
			numUnits,
			string(configYAML),
			c.Constraints,
			c.ToMachineSpec,
			requestedNetworks,
			c.Storage,
		)
		if params.IsCodeNotImplemented(err) {
			return notSupported
		}
		return block.ProcessBlockedError(err, block.BlockChange)
	}

	err = client.ServiceDeployWithNetworks(
		curl.String(),
		serviceName,
		numUnits,
		string(configYAML),
		c.Constraints,
		c.ToMachineSpec,
		requestedNetworks,
	)
	if params.IsCodeNotImplemented(err) {
		if haveNetworks {
			return errors.New("cannot use --networks/--constraints networks=...: not supported by the API server")
		}
		err = client.ServiceDeploy(
			curl.String(),
			serviceName,
			numUnits,
			string(configYAML),
			c.Constraints,
			c.ToMachineSpec)
	}

	if err != nil {
		return block.ProcessBlockedError(err, block.BlockChange)
	}

	state, err := c.NewAPIRoot()
	if err != nil {
		return err
	}
	err = registerMeteredCharm(c.RegisterURL, state, csClient.jar, curl.String(), serviceName, client.EnvironmentUUID())
	if err != nil {
		return err
	}

	return block.ProcessBlockedError(err, block.BlockChange)
}
Beispiel #29
0
// Run implements Command.Run
func (c *killCommand) Run(ctx *cmd.Context) error {
	if c.apiDialerFunc == nil {
		c.apiDialerFunc = c.NewAPIRoot
	}

	store, err := configstore.Default()
	if err != nil {
		return errors.Annotate(err, "cannot open system info storage")
	}

	cfgInfo, err := store.ReadInfo(c.systemName)
	if err != nil {
		return errors.Annotate(err, "cannot read system info")
	}

	// Verify that we're destroying a system
	apiEndpoint := cfgInfo.APIEndpoint()
	if apiEndpoint.ServerUUID != "" && apiEndpoint.EnvironUUID != apiEndpoint.ServerUUID {
		return errors.Errorf("%q is not a system; use juju environment destroy to destroy it", c.systemName)
	}

	if !c.assumeYes {
		if err = confirmDestruction(ctx, c.systemName); err != nil {
			return err
		}
	}

	// Attempt to connect to the API.
	api, err := c.getSystemAPI(cfgInfo)
	switch {
	case err == nil:
		defer api.Close()
	case errors.Cause(err) == common.ErrPerm:
		return errors.Annotate(err, "cannot destroy system")
	default:
		if err != ErrConnTimedOut {
			logger.Debugf("unable to open api: %s", err)
		}
		ctx.Infof("Unable to open API: %s\n", err)
		api = nil
	}

	// Obtain bootstrap / system environ information
	systemEnviron, err := c.getSystemEnviron(cfgInfo, api)
	if err != nil {
		return errors.Annotate(err, "cannot obtain bootstrap information")
	}

	// If we were unable to connect to the API, just destroy the system through
	// the environs interface.
	if api == nil {
		return environs.Destroy(systemEnviron, store)
	}

	// Attempt to destroy the system with destroyEnvs and ignoreBlocks = true
	err = api.DestroySystem(true, true)
	if params.IsCodeNotImplemented(err) {
		// Fall back to using the client endpoint to destroy the system,
		// sending the info we were already able to collect.
		return c.killSystemViaClient(ctx, cfgInfo, systemEnviron, store)
	}

	if err != nil {
		ctx.Infof("Unable to destroy system through the API: %s.  Destroying through provider.", err)
	}

	return environs.Destroy(systemEnviron, store)
}
Beispiel #30
0
func (c *addCommand) Run(ctx *cmd.Context) error {
	client, err := c.getClientAPI()
	if err != nil {
		return errors.Trace(err)
	}
	defer client.Close()

	var machineManager MachineManagerAPI
	if len(c.Disks) > 0 {
		machineManager, err = c.getMachineManagerAPI()
		if err != nil {
			return errors.Trace(err)
		}
		defer machineManager.Close()
		if machineManager.BestAPIVersion() < 1 {
			return errors.New("cannot add machines with disks: not supported by the API server")
		}
	}

	logger.Infof("load config")
	var config *config.Config
	if defaultStore, err := configstore.Default(); err != nil {
		return err
	} else if config, err = c.Config(defaultStore, client); err != nil {
		return err
	}

	if c.Placement != nil && c.Placement.Scope == "ssh" {
		logger.Infof("manual provisioning")
		args := manual.ProvisionMachineArgs{
			Host:   c.Placement.Directive,
			Client: client,
			Stdin:  ctx.Stdin,
			Stdout: ctx.Stdout,
			Stderr: ctx.Stderr,
			UpdateBehavior: &params.UpdateBehavior{
				config.EnableOSRefreshUpdate(),
				config.EnableOSUpgrade(),
			},
		}
		machineId, err := manualProvisioner(args)
		if err == nil {
			ctx.Infof("created machine %v", machineId)
		}
		return err
	}

	logger.Infof("environment provisioning")
	if c.Placement != nil && c.Placement.Scope == "env-uuid" {
		c.Placement.Scope = client.EnvironmentUUID()
	}

	if c.Placement != nil && c.Placement.Scope == instance.MachineScope {
		// It does not make sense to add-machine <id>.
		return fmt.Errorf("machine-id cannot be specified when adding machines")
	}

	jobs := []multiwatcher.MachineJob{multiwatcher.JobHostUnits}

	envVersion, err := envcmd.GetEnvironmentVersion(client)
	if err != nil {
		return err
	}

	// Servers before 1.21-alpha2 don't have the networker so don't
	// try to use JobManageNetworking with them.
	//
	// In case of MAAS and Joyent JobManageNetworking is not added
	// to ensure the non-intrusive start of a networker like above
	// for the manual provisioning. See this related joyent bug
	// http://pad.lv/1401423
	if envVersion.Compare(version.MustParse("1.21-alpha2")) >= 0 &&
		config.Type() != provider.MAAS &&
		config.Type() != provider.Joyent {
		jobs = append(jobs, multiwatcher.JobManageNetworking)
	}

	machineParams := params.AddMachineParams{
		Placement:   c.Placement,
		Series:      c.Series,
		Constraints: c.Constraints,
		Jobs:        jobs,
		Disks:       c.Disks,
	}
	machines := make([]params.AddMachineParams, c.NumMachines)
	for i := 0; i < c.NumMachines; i++ {
		machines[i] = machineParams
	}

	var results []params.AddMachinesResult
	// If storage is specified, we attempt to use a new API on the service facade.
	if len(c.Disks) > 0 {
		results, err = machineManager.AddMachines(machines)
	} else {
		results, err = client.AddMachines(machines)
		if params.IsCodeNotImplemented(err) {
			if c.Placement != nil {
				containerType, parseErr := instance.ParseContainerType(c.Placement.Scope)
				if parseErr != nil {
					// The user specified a non-container placement directive:
					// return original API not implemented error.
					return err
				}
				machineParams.ContainerType = containerType
				machineParams.ParentId = c.Placement.Directive
				machineParams.Placement = nil
			}
			logger.Infof(
				"AddMachinesWithPlacement not supported by the API server, " +
					"falling back to 1.18 compatibility mode",
			)
			results, err = client.AddMachines1dot18([]params.AddMachineParams{machineParams})
		}
	}
	if params.IsCodeOperationBlocked(err) {
		return block.ProcessBlockedError(err, block.BlockChange)
	}
	if err != nil {
		return errors.Trace(err)
	}

	errs := []error{}
	for _, machineInfo := range results {
		if machineInfo.Error != nil {
			errs = append(errs, machineInfo.Error)
			continue
		}
		machineId := machineInfo.Machine

		if names.IsContainerMachine(machineId) {
			ctx.Infof("created container %v", machineId)
		} else {
			ctx.Infof("created machine %v", machineId)
		}
	}
	if len(errs) == 1 {
		fmt.Fprintf(ctx.Stderr, "failed to create 1 machine\n")
		return errs[0]
	}
	if len(errs) > 1 {
		fmt.Fprintf(ctx.Stderr, "failed to create %d machines\n", len(errs))
		returnErr := []string{}
		for _, e := range errs {
			returnErr = append(returnErr, e.Error())
		}
		return errors.New(strings.Join(returnErr, ", "))
	}
	return nil
}