本文整理汇总了Golang中github.com/juju/juju/apiserver/params.IsCodeNotProvisioned函数的典型用法代码示例。如果您正苦于以下问题:Golang IsCodeNotProvisioned函数的具体用法?Golang IsCodeNotProvisioned怎么用?Golang IsCodeNotProvisioned使用的例子?那么恭喜您, 这里精选的函数代码示例或许可以为您提供帮助。
在下文中一共展示了IsCodeNotProvisioned函数的15个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于系统推荐出更棒的Golang代码示例。
示例1: connectFallback
// connectFallback opens an API connection using the supplied info,
// or a copy using the fallbackPassword; blocks for up to 5 minutes
// if it encounters a CodeNotProvisioned error, periodically retrying;
// and eventually, having either succeeded, failed, or timed out, returns:
//
// * (if successful) the connection, and whether the fallback was used
// * (otherwise) whatever error it most recently encountered
//
// It's clear that it still has machine-agent concerns still baked in,
// but there's no obvious practical path to separating those entirely at
// the moment.
//
// (The right answer is probably to treat CodeNotProvisioned as a normal
// error and depend on (currently nonexistent) exponential backoff in
// the framework: either it'll work soon enough, or the controller will
// spot the error and nuke the machine anyway. No harm leaving the local
// agent running and occasionally polling for changes -- it won't do much
// until it's managed to log in, and any suicide-cutoff point we pick here
// will be objectively bad in some circumstances.)
func connectFallback(
apiOpen api.OpenFunc, info *api.Info, fallbackPassword string,
) (
conn api.Connection, didFallback bool, err error,
) {
// We expect to assign to `conn`, `err`, *and* `info` in
// the course of this operation: wrapping this repeated
// atom in a func currently seems to be less treacherous
// than the alternatives.
var tryConnect = func() {
conn, err = apiOpen(info, api.DialOpts{})
}
// Try to connect, trying both the primary and fallback
// passwords if necessary; and update info, and remember
// which password we used.
tryConnect()
if params.IsCodeUnauthorized(err) {
// We've perhaps used the wrong password, so
// try again with the fallback password.
infoCopy := *info
info = &infoCopy
info.Password = fallbackPassword
didFallback = true
tryConnect()
}
// We might be a machine agent that's started before its
// provisioner has had a chance to report instance data
// to the machine; wait a fair while to ensure we really
// are in the (expected rare) provisioner-crash situation
// that would cause permanent CodeNotProvisioned (which
// indicates that the controller has forgotten about us,
// and is provisioning a new instance, so we really should
// uninstall).
//
// Yes, it's dumb that this can't be interrupted, and that
// it's not configurable without patching.
if params.IsCodeNotProvisioned(err) {
for a := checkProvisionedStrategy.Start(); a.Next(); {
tryConnect()
if !params.IsCodeNotProvisioned(err) {
break
}
}
}
// At this point we've run out of reasons to retry connecting,
// and just go with whatever error we last saw (if any).
if err != nil {
return nil, false, errors.Trace(err)
}
return conn, didFallback, nil
}
示例2: pendingOrDeadOrMaintain
// pendingOrDead looks up machines with ids and returns those that do not
// have an instance id assigned yet, and also those that are dead.
func (task *provisionerTask) pendingOrDeadOrMaintain(ids []string) (pending, dead, maintain []*apiprovisioner.Machine, err error) {
for _, id := range ids {
machine, found := task.machines[id]
if !found {
logger.Infof("machine %q not found", id)
continue
}
switch machine.Life() {
case params.Dying:
if _, err := machine.InstanceId(); err == nil {
continue
} else if !params.IsCodeNotProvisioned(err) {
return nil, nil, nil, errors.Annotatef(err, "failed to load machine %q instance id: %v", machine)
}
logger.Infof("killing dying, unprovisioned machine %q", machine)
if err := machine.EnsureDead(); err != nil {
return nil, nil, nil, errors.Annotatef(err, "failed to ensure machine dead %q: %v", machine)
}
fallthrough
case params.Dead:
dead = append(dead, machine)
continue
}
if instId, err := machine.InstanceId(); err != nil {
if !params.IsCodeNotProvisioned(err) {
logger.Errorf("failed to load machine %q instance id: %v", machine, err)
continue
}
status, _, err := machine.Status()
if err != nil {
logger.Infof("cannot get machine %q status: %v", machine, err)
continue
}
if status == params.StatusPending {
pending = append(pending, machine)
logger.Infof("found machine %q pending provisioning", machine)
continue
}
} else {
logger.Infof("machine %v already started as instance %q", machine, instId)
if err != nil {
logger.Infof("Error fetching provisioning info")
} else {
isLxc := regexp.MustCompile(`\d+/lxc/\d+`)
if isLxc.MatchString(machine.Id()) {
maintain = append(maintain, machine)
}
}
}
}
logger.Tracef("pending machines: %v", pending)
logger.Tracef("dead machines: %v", dead)
return
}
示例3: refreshMachine
// refreshMachine refreshes the specified machine's instance ID. If it is set,
// then the machine watcher is stopped and pending entities' parameters are
// updated. If the machine is not provisioned yet, this method is a no-op.
func refreshMachine(ctx *context, tag names.MachineTag) error {
w, ok := ctx.machines[tag]
if !ok {
return errors.Errorf("machine %s is not being watched", tag.Id())
}
stopAndRemove := func() error {
worker.Stop(w)
delete(ctx.machines, tag)
return nil
}
results, err := ctx.config.Machines.InstanceIds([]names.MachineTag{tag})
if err != nil {
return errors.Annotate(err, "getting machine instance ID")
}
if err := results[0].Error; err != nil {
if params.IsCodeNotProvisioned(err) {
return nil
} else if params.IsCodeNotFound(err) {
// Machine is gone, so stop watching.
return stopAndRemove()
}
return errors.Annotate(err, "getting machine instance ID")
}
machineProvisioned(ctx, tag, instance.Id(results[0].Result))
// machine provisioning is the only thing we care about;
// stop the watcher.
return stopAndRemove()
}
示例4: RestoreError
// RestoreError makes a best effort at converting the given error
// back into an error originally converted by ServerError(). If the
// error could not be converted then false is returned.
func RestoreError(err error) (error, bool) {
err = errors.Cause(err)
if apiErr, ok := err.(*params.Error); !ok {
return err, false
} else if apiErr == nil {
return nil, true
}
if params.ErrCode(err) == "" {
return err, false
}
msg := err.Error()
if singleton, ok := singletonError(err); ok {
return singleton, true
}
// TODO(ericsnow) Support the other error types handled by ServerError().
switch {
case params.IsCodeUnauthorized(err):
return errors.NewUnauthorized(nil, msg), true
case params.IsCodeNotFound(err):
// TODO(ericsnow) UnknownModelError should be handled here too.
// ...by parsing msg?
return errors.NewNotFound(nil, msg), true
case params.IsCodeAlreadyExists(err):
return errors.NewAlreadyExists(nil, msg), true
case params.IsCodeNotAssigned(err):
return errors.NewNotAssigned(nil, msg), true
case params.IsCodeHasAssignedUnits(err):
// TODO(ericsnow) Handle state.HasAssignedUnitsError here.
// ...by parsing msg?
return err, false
case params.IsCodeNoAddressSet(err):
// TODO(ericsnow) Handle isNoAddressSetError here.
// ...by parsing msg?
return err, false
case params.IsCodeNotProvisioned(err):
return errors.NewNotProvisioned(nil, msg), true
case params.IsCodeUpgradeInProgress(err):
// TODO(ericsnow) Handle state.UpgradeInProgressError here.
// ...by parsing msg?
return err, false
case params.IsCodeMachineHasAttachedStorage(err):
// TODO(ericsnow) Handle state.HasAttachmentsError here.
// ...by parsing msg?
return err, false
case params.IsCodeNotSupported(err):
return errors.NewNotSupported(nil, msg), true
case params.IsBadRequest(err):
return errors.NewBadRequest(nil, msg), true
case params.IsMethodNotAllowed(err):
return errors.NewMethodNotAllowed(nil, msg), true
case params.ErrCode(err) == params.CodeDischargeRequired:
// TODO(ericsnow) Handle DischargeRequiredError here.
return err, false
default:
return err, false
}
}
示例5: loop
func (s *storageAttachmentWatcher) loop() error {
for {
select {
case <-s.catacomb.Dying():
return s.catacomb.ErrDying()
case _, ok := <-s.changes:
if !ok {
return errors.New("storage attachment watcher closed")
}
snapshot, err := getStorageSnapshot(
s.st, s.storageTag, s.unitTag,
)
if params.IsCodeNotFound(err) {
// The storage attachment was removed
// from state, so we can stop watching.
return nil
} else if params.IsCodeNotProvisioned(err) {
// We do not care about unattached
// storage here.
continue
} else if err != nil {
return err
}
change := storageAttachmentChange{
s.storageTag,
snapshot,
}
select {
case <-s.catacomb.Dying():
return s.catacomb.ErrDying()
case s.out <- change:
}
}
}
}
示例6: findUnknownInstances
// findUnknownInstances finds instances which are not associated with a machine.
func (task *provisionerTask) findUnknownInstances(stopping []instance.Instance) ([]instance.Instance, error) {
// Make a copy of the instances we know about.
instances := make(map[instance.Id]instance.Instance)
for k, v := range task.instances {
instances[k] = v
}
for _, m := range task.machines {
instId, err := m.InstanceId()
switch {
case err == nil:
delete(instances, instId)
case params.IsCodeNotProvisioned(err):
case params.IsCodeNotFoundOrCodeUnauthorized(err):
default:
return nil, err
}
}
// Now remove all those instances that we are stopping already as we
// know about those and don't want to include them in the unknown list.
for _, inst := range stopping {
delete(instances, inst.Id())
}
var unknown []instance.Instance
for _, inst := range instances {
unknown = append(unknown, inst)
}
return unknown, nil
}
示例7: watchStorageAttachment
// watchStorageAttachment starts watching the storage attachment with
// the specified storage tag, waits for its first event, and records
// the information in the current snapshot.
func (w *RemoteStateWatcher) watchStorageAttachment(
tag names.StorageTag,
life params.Life,
saw watcher.NotifyWatcher,
) error {
var storageSnapshot StorageSnapshot
select {
case <-w.catacomb.Dying():
return w.catacomb.ErrDying()
case _, ok := <-saw.Changes():
if !ok {
return errors.New("storage attachment watcher closed")
}
var err error
storageSnapshot, err = getStorageSnapshot(w.st, tag, w.unit.Tag())
if params.IsCodeNotProvisioned(err) {
// If the storage is unprovisioned, we still want to
// record the attachment, but we'll mark it as
// unattached. This allows the uniter to wait for
// pending storage attachments to be provisioned.
storageSnapshot = StorageSnapshot{Life: life}
} else if err != nil {
return errors.Annotatef(err, "processing initial storage attachment change")
}
}
innerSAW, err := newStorageAttachmentWatcher(
w.st, saw, w.unit.Tag(), tag, w.storageAttachmentChanges,
)
if err != nil {
return errors.Trace(err)
}
w.current.Storage[tag] = storageSnapshot
w.storageAttachmentWatchers[tag] = innerSAW
return nil
}
示例8: processAliveFilesystems
// processAliveFilesystems processes the FilesystemResults for Alive filesystems,
// provisioning filesystems and setting the info in state as necessary.
func processAliveFilesystems(ctx *context, tags []names.FilesystemTag, filesystemResults []params.FilesystemResult) error {
// Filter out the already-provisioned filesystems.
pending := make([]names.FilesystemTag, 0, len(tags))
for i, result := range filesystemResults {
tag := tags[i]
if result.Error == nil {
// Filesystem is already provisioned: skip.
logger.Debugf("filesystem %q is already provisioned, nothing to do", tag.Id())
filesystem, err := filesystemFromParams(result.Result)
if err != nil {
return errors.Annotate(err, "getting filesystem info")
}
ctx.filesystems[tag] = filesystem
if filesystem.Volume != (names.VolumeTag{}) {
// Ensure that volume-backed filesystems' block
// devices are present even after creating the
// filesystem, so that attachments can be made.
maybeAddPendingVolumeBlockDevice(ctx, filesystem.Volume)
}
continue
}
if !params.IsCodeNotProvisioned(result.Error) {
return errors.Annotatef(
result.Error, "getting filesystem information for filesystem %q", tag.Id(),
)
}
// The filesystem has not yet been provisioned, so record its tag
// to enquire about parameters below.
pending = append(pending, tag)
}
if len(pending) == 0 {
return nil
}
paramsResults, err := ctx.filesystemAccessor.FilesystemParams(pending)
if err != nil {
return errors.Annotate(err, "getting filesystem params")
}
for i, result := range paramsResults {
if result.Error != nil {
return errors.Annotate(result.Error, "getting filesystem parameters")
}
params, err := filesystemParamsFromParams(result.Result)
if err != nil {
return errors.Annotate(err, "getting filesystem parameters")
}
ctx.pendingFilesystems[pending[i]] = params
if params.Volume != (names.VolumeTag{}) {
// The filesystem is volume-backed: we must watch for
// the corresponding block device. This will trigger a
// one-time (for the volume) forced update of block
// devices. If the block device is not immediately
// available, then we rely on the watcher. The forced
// update is necessary in case the block device was
// added to state already, and we didn't observe it.
maybeAddPendingVolumeBlockDevice(ctx, params.Volume)
}
}
return nil
}
示例9: classifyMachine
func classifyMachine(machine ClassifiableMachine) (
MachineClassification, error) {
switch machine.Life() {
case params.Dying:
if _, err := machine.InstanceId(); err == nil {
return None, nil
} else if !params.IsCodeNotProvisioned(err) {
return None, errors.Annotatef(err, "failed to load dying machine id:%s, details:%v", machine.Id(), machine)
}
logger.Infof("killing dying, unprovisioned machine %q", machine)
if err := machine.EnsureDead(); err != nil {
return None, errors.Annotatef(err, "failed to ensure machine dead id:%s, details:%v", machine.Id(), machine)
}
fallthrough
case params.Dead:
return Dead, nil
}
if instId, err := machine.InstanceId(); err != nil {
if !params.IsCodeNotProvisioned(err) {
return None, errors.Annotatef(err, "failed to load machine id:%s, details:%v", machine.Id(), machine)
}
status, _, err := machine.Status()
if err != nil {
logger.Infof("cannot get machine id:%s, details:%v, err:%v", machine.Id(), machine, err)
return None, nil
}
if status == params.StatusPending {
logger.Infof("found machine pending provisioning id:%s, details:%v", machine.Id(), machine)
return Pending, nil
}
} else {
logger.Infof("machine %s already started as instance %q", machine.Id(), instId)
if err != nil {
logger.Infof("Error fetching provisioning info")
} else {
isLxc := regexp.MustCompile(`\d+/lxc/\d+`)
isKvm := regexp.MustCompile(`\d+/kvm/\d+`)
if isLxc.MatchString(machine.Id()) || isKvm.MatchString(machine.Id()) {
return Maintain, nil
}
}
}
return None, nil
}
示例10: machineLoop
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
}
}
}
}
示例11: pollInstanceInfo
// 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
}
示例12: classifyMachine
func classifyMachine(machine ClassifiableMachine) (
MachineClassification, error) {
switch machine.Life() {
case params.Dying:
if _, err := machine.InstanceId(); err == nil {
return None, nil
} else if !params.IsCodeNotProvisioned(err) {
return None, errors.Annotatef(err, "failed to load dying machine id:%s, details:%v", machine.Id(), machine)
}
logger.Infof("killing dying, unprovisioned machine %q", machine)
if err := machine.EnsureDead(); err != nil {
return None, errors.Annotatef(err, "failed to ensure machine dead id:%s, details:%v", machine.Id(), machine)
}
fallthrough
case params.Dead:
return Dead, nil
}
instId, err := machine.InstanceId()
if err != nil {
if !params.IsCodeNotProvisioned(err) {
return None, errors.Annotatef(err, "failed to load machine id:%s, details:%v", machine.Id(), machine)
}
machineStatus, _, err := machine.Status()
if err != nil {
logger.Infof("cannot get machine id:%s, details:%v, err:%v", machine.Id(), machine, err)
return None, nil
}
if machineStatus == status.Pending {
logger.Infof("found machine pending provisioning id:%s, details:%v", machine.Id(), machine)
return Pending, nil
}
return None, nil
}
logger.Infof("machine %s already started as instance %q", machine.Id(), instId)
if state.ContainerTypeFromId(machine.Id()) != "" {
return Maintain, nil
}
return None, nil
}
示例13: machineLoop
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) {
return err
}
machineStatus := status.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
}
}
// the extra condition below (checking allocating/pending) is here to improve user experience
// without it the instance status will say "pending" for +10 minutes after the agent comes up to "started"
if instInfo.status.Status != status.StatusAllocating && instInfo.status.Status != status.StatusPending {
if len(instInfo.addresses) > 0 && machineStatus == status.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 <-context.dying():
return context.errDying()
case <-time.After(pollInterval):
// TODO(fwereade): 2016-03-17 lp:1558657
pollInstance = true
case <-changed:
if err := m.Refresh(); err != nil {
return err
}
if m.Life() == params.Dead {
return nil
}
}
}
}
示例14: refreshVolumeBlockDevices
// refreshVolumeBlockDevices refreshes the block devices for the specified
// volumes.
func refreshVolumeBlockDevices(ctx *context, volumeTags []names.VolumeTag) error {
machineTag, ok := ctx.config.Scope.(names.MachineTag)
if !ok {
// This function should only be called by machine-scoped
// storage provisioners.
panic(errors.New("expected machine tag"))
}
ids := make([]params.MachineStorageId, len(volumeTags))
for i, volumeTag := range volumeTags {
ids[i] = params.MachineStorageId{
MachineTag: machineTag.String(),
AttachmentTag: volumeTag.String(),
}
}
results, err := ctx.config.Volumes.VolumeBlockDevices(ids)
if err != nil {
return errors.Annotate(err, "refreshing volume block devices")
}
for i, result := range results {
if result.Error == nil {
ctx.volumeBlockDevices[volumeTags[i]] = result.Result
for _, params := range ctx.incompleteFilesystemParams {
if params.Volume == volumeTags[i] {
updatePendingFilesystem(ctx, params)
}
}
for id, params := range ctx.incompleteFilesystemAttachmentParams {
filesystem, ok := ctx.filesystems[params.Filesystem]
if !ok {
continue
}
if filesystem.Volume == volumeTags[i] {
updatePendingFilesystemAttachment(ctx, id, params)
}
}
} else if params.IsCodeNotProvisioned(result.Error) || params.IsCodeNotFound(result.Error) {
// Either the volume (attachment) isn't provisioned,
// or the corresponding block device is not yet known.
//
// Neither of these errors is fatal; we just wait for
// the block device watcher to notify us again.
} else {
return errors.Annotatef(
err, "getting block device info for volume attachment %v",
ids[i],
)
}
}
return nil
}
示例15: openAPIStateUsingInfo
func openAPIStateUsingInfo(info *api.Info, oldPassword string) (api.Connection, bool, error) {
// We let the API dial fail immediately because the
// runner's loop outside the caller of openAPIState will
// keep on retrying. If we block for ages here,
// then the worker that's calling this cannot
// be interrupted.
st, err := apiOpen(info, api.DialOpts{})
usedOldPassword := false
if params.IsCodeUnauthorized(err) {
// We've perhaps used the wrong password, so
// try again with the fallback password.
infoCopy := *info
info = &infoCopy
info.Password = oldPassword
usedOldPassword = true
st, err = apiOpen(info, api.DialOpts{})
}
// The provisioner may take some time to record the agent's
// machine instance ID, so wait until it does so.
if params.IsCodeNotProvisioned(err) {
for a := checkProvisionedStrategy.Start(); a.Next(); {
st, err = apiOpen(info, api.DialOpts{})
if !params.IsCodeNotProvisioned(err) {
break
}
}
}
if err != nil {
if params.IsCodeNotProvisioned(err) || params.IsCodeUnauthorized(err) {
logger.Errorf("agent terminating due to error returned during API open: %v", err)
return nil, false, worker.ErrTerminateAgent
}
return nil, false, err
}
return st, usedOldPassword, nil
}