本文整理汇总了Golang中github.com/juju/juju/apiserver/params.IsCodeUnauthorized函数的典型用法代码示例。如果您正苦于以下问题:Golang IsCodeUnauthorized函数的具体用法?Golang IsCodeUnauthorized怎么用?Golang IsCodeUnauthorized使用的例子?那么恭喜您, 这里精选的函数代码示例或许可以为您提供帮助。
在下文中一共展示了IsCodeUnauthorized函数的15个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于系统推荐出更棒的Golang代码示例。
示例1: getModelInfo
func (c *modelsCommand) getModelInfo(userModels []base.UserModel) ([]params.ModelInfo, error) {
client, err := c.getModelManagerAPI()
if err != nil {
return nil, errors.Trace(err)
}
defer client.Close()
tags := make([]names.ModelTag, len(userModels))
for i, m := range userModels {
tags[i] = names.NewModelTag(m.UUID)
}
results, err := client.ModelInfo(tags)
if err != nil {
return nil, errors.Trace(err)
}
info := make([]params.ModelInfo, len(tags))
for i, result := range results {
if result.Error != nil {
if params.IsCodeUnauthorized(result.Error) {
// If we get this, then the model was removed
// between the initial listing and the call
// to query its details.
continue
}
return nil, errors.Annotatef(
result.Error, "getting model %s (%q) info",
userModels[i].UUID, userModels[i].Name,
)
}
info[i] = *result.Result
}
return info, nil
}
示例2: 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
}
}
示例3: addCharmViaAPI
// addCharmViaAPI calls the appropriate client API calls to add the
// given charm URL to state. For non-public charm URLs, this function also
// handles the macaroon authorization process using the given csClient.
// The resulting charm URL of the added charm is displayed on stdout.
func addCharmViaAPI(client *api.Client, ctx *cmd.Context, curl *charm.URL, repo charmrepo.Interface, csclient *csClient) (*charm.URL, error) {
switch curl.Schema {
case "local":
ch, err := repo.Get(curl)
if err != nil {
return nil, err
}
stateCurl, err := client.AddLocalCharm(curl, ch)
if err != nil {
return nil, err
}
curl = stateCurl
case "cs":
if err := client.AddCharm(curl); err != nil {
if !params.IsCodeUnauthorized(err) {
return nil, errors.Mask(err)
}
m, err := csclient.authorize(curl)
if err != nil {
return nil, errors.Mask(err)
}
if err := client.AddCharmWithAuthorization(curl, m); err != nil {
return nil, errors.Mask(err)
}
}
default:
return nil, fmt.Errorf("unsupported charm URL schema: %q", curl.Schema)
}
ctx.Infof("Added charm %q to the environment.", curl)
return curl, nil
}
示例4: Run
// Run implements Command.Run.
func (c *addCommand) Run(ctx *cmd.Context) error {
return c.RunWithAPI(ctx, func(api SpaceAPI, ctx *cmd.Context) error {
// Prepare a nicer message and proper arguments to use in case
// there are not CIDRs given.
var subnetIds []string
msgSuffix := "no subnets"
if !c.CIDRs.IsEmpty() {
subnetIds = c.CIDRs.SortedValues()
msgSuffix = fmt.Sprintf("subnets %s", strings.Join(subnetIds, ", "))
}
// Add the new space.
// TODO(dimitern): Accept --public|--private and pass it here.
err := api.AddSpace(c.Name, subnetIds, true)
if err != nil {
if errors.IsNotSupported(err) {
ctx.Infof("cannot add space %q: %v", c.Name, err)
}
if params.IsCodeUnauthorized(err) {
common.PermissionsMessage(ctx.Stderr, "add a space")
}
return errors.Annotatef(err, "cannot add space %q", c.Name)
}
ctx.Infof("added space %q with %s", c.Name, msgSuffix)
return nil
})
}
示例5: addCharmFromURL
// addCharmFromURL calls the appropriate client API calls to add the
// given charm URL to state. For non-public charm URLs, this function also
// handles the macaroon authorization process using the given csClient.
// The resulting charm URL of the added charm is displayed on stdout.
func addCharmFromURL(client *api.Client, curl *charm.URL, repo charmrepo.Interface, csclient *csClient) (*charm.URL, error) {
switch curl.Schema {
case "local":
ch, err := repo.Get(curl)
if err != nil {
return nil, err
}
stateCurl, err := client.AddLocalCharm(curl, ch)
if err != nil {
return nil, err
}
curl = stateCurl
case "cs":
if err := client.AddCharm(curl); err != nil {
if !params.IsCodeUnauthorized(err) {
return nil, errors.Trace(err)
}
m, err := csclient.authorize(curl)
if err != nil {
return nil, maybeTermsAgreementError(err)
}
if err := client.AddCharmWithAuthorization(curl, m); err != nil {
return nil, errors.Trace(err)
}
}
default:
return nil, fmt.Errorf("unsupported charm URL schema: %q", curl.Schema)
}
return curl, nil
}
示例6: OpenAPIState
// OpenAPIState opens the API using the given information. The agent's
// password is changed if the fallback password was used to connect to
// the API.
func OpenAPIState(agentConfig agent.Config, a Agent) (_ *api.State, _ *apiagent.Entity, outErr error) {
info := agentConfig.APIInfo()
st, usedOldPassword, err := openAPIStateUsingInfo(info, a, agentConfig.OldPassword())
if err != nil {
return nil, nil, err
}
defer func() {
if outErr != nil && st != nil {
st.Close()
}
}()
entity, err := st.Agent().Entity(a.Tag())
if err == nil && entity.Life() == params.Dead {
logger.Errorf("agent terminating - entity %q is dead", a.Tag())
return nil, nil, worker.ErrTerminateAgent
}
if params.IsCodeUnauthorized(err) {
logger.Errorf("agent terminating due to error returned during entity lookup: %v", err)
return nil, nil, worker.ErrTerminateAgent
}
if err != nil {
return nil, nil, err
}
if !usedOldPassword {
// Call set password with the current password. If we've recently
// become a state server, this will fix up our credentials in mongo.
if err := entity.SetPassword(info.Password); err != nil {
return nil, nil, errors.Annotate(err, "can't reset agent password")
}
} else {
// We succeeded in connecting with the fallback
// password, so we need to create a new password
// for the future.
newPassword, err := utils.RandomPassword()
if err != nil {
return nil, nil, err
}
err = setAgentPassword(newPassword, info.Password, a, entity)
if err != nil {
return nil, nil, err
}
// Reconnect to the API with the new password.
st.Close()
info.Password = newPassword
st, err = apiOpen(info, api.DialOpts{})
if err != nil {
return nil, nil, err
}
}
return st, entity, err
}
示例7: 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
}
示例8: Run
func (c *addRelationCommand) Run(ctx *cmd.Context) error {
client, err := c.newAPIFunc()
if err != nil {
return err
}
defer client.Close()
_, err = client.AddRelation(c.Endpoints...)
if params.IsCodeUnauthorized(err) {
common.PermissionsMessage(ctx.Stderr, "add a relation")
}
return block.ProcessBlockedError(err, block.BlockChange)
}
示例9: 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
}
示例10: opClientResolved
func opClientResolved(c *gc.C, st api.Connection, _ *state.State) (func(), error) {
err := st.Client().Resolved("wordpress/1", false)
// There are several scenarios in which this test is called, one is
// that the user is not authorized. In that case we want to exit now,
// letting the error percolate out so the caller knows that the
// permission error was correctly generated.
if err != nil && params.IsCodeUnauthorized(err) {
return func() {}, err
}
// Otherwise, the user was authorized, but we expect an error anyway
// because the unit is not in an error state when we tried to resolve
// the error. Therefore, since it is complaining it means that the
// call to Resolved worked, so we're happy.
c.Assert(err, gc.NotNil)
c.Assert(err.Error(), gc.Equals, `unit "wordpress/1" is not in an error state`)
return func() {}, nil
}
示例11: addCharmFromURL
// addCharmFromURL calls the appropriate client API calls to add the
// given charm URL to state. For non-public charm URLs, this function also
// handles the macaroon authorization process using the given csClient.
// The resulting charm URL of the added charm is displayed on stdout.
func addCharmFromURL(client *api.Client, curl *charm.URL, channel csparams.Channel, csClient *csclient.Client) (*charm.URL, *macaroon.Macaroon, error) {
var csMac *macaroon.Macaroon
if err := client.AddCharm(curl, channel); err != nil {
if !params.IsCodeUnauthorized(err) {
return nil, nil, errors.Trace(err)
}
m, err := authorizeCharmStoreEntity(csClient, curl)
if err != nil {
return nil, nil, maybeTermsAgreementError(err)
}
if err := client.AddCharmWithAuthorization(curl, channel, m); err != nil {
return nil, nil, errors.Trace(err)
}
csMac = m
}
return curl, csMac, nil
}
示例12: addCharmFromURL
// addCharmFromURL calls the appropriate client API calls to add the
// given charm URL to state. For non-public charm URLs, this function also
// handles the macaroon authorization process using the given csClient.
// The resulting charm URL of the added charm is displayed on stdout.
//
// The repo holds the charm repository associated with with the URL
// by resolveCharmStoreEntityURL.
func addCharmFromURL(client *api.Client, curl *charm.URL, channel csparams.Channel, repo charmrepo.Interface) (*charm.URL, *macaroon.Macaroon, error) {
var csMac *macaroon.Macaroon
switch curl.Schema {
case "local":
ch, err := repo.Get(curl)
if err != nil {
return nil, nil, err
}
stateCurl, err := client.AddLocalCharm(curl, ch)
if err != nil {
return nil, nil, err
}
curl = stateCurl
case "cs":
repo, ok := repo.(*charmrepo.CharmStore)
if !ok {
return nil, nil, errors.Errorf("(cannot happen) cs-schema URL with unexpected repo type %T", repo)
}
csClient := repo.Client()
if err := client.AddCharm(curl, channel); err != nil {
if !params.IsCodeUnauthorized(err) {
return nil, nil, errors.Trace(err)
}
m, err := authorizeCharmStoreEntity(csClient, curl)
if err != nil {
return nil, nil, maybeTermsAgreementError(err)
}
if err := client.AddCharmWithAuthorization(curl, channel, m); err != nil {
return nil, nil, errors.Trace(err)
}
csMac = m
}
default:
return nil, nil, fmt.Errorf("unsupported charm URL schema: %q", curl.Schema)
}
return curl, csMac, nil
}
示例13: ScaryConnect
// ScaryConnect logs into the API using the supplied agent's credentials,
// like OnlyConnect; and then:
//
// * returns ErrConnectImpossible if the agent entity is dead or
// unauthorized for all known passwords;
// * if the agent's config does not specify a model, tries to record the
// model we just connected to;
// * replaces insecure credentials with freshly (locally) generated ones
// (and returns ErrPasswordChanged, expecting to be reinvoked);
// * unconditionally resets the remote-state password to its current value
// (for what seems like a bad reason).
//
// This is clearly a mess but at least now it's a documented and localized
// mess; it should be used only when making the primary API connection for
// a machine or unit agent running in its own process.
func ScaryConnect(a agent.Agent, apiOpen api.OpenFunc) (_ api.Connection, err error) {
agentConfig := a.CurrentConfig()
info, ok := agentConfig.APIInfo()
if !ok {
return nil, errors.New("API info not available")
}
oldPassword := agentConfig.OldPassword()
defer func() {
cause := errors.Cause(err)
switch {
case cause == apiagent.ErrDenied:
case cause == errAgentEntityDead:
case params.IsCodeUnauthorized(cause):
case params.IsCodeNotProvisioned(cause):
default:
return
}
err = ErrConnectImpossible
}()
// Start connection...
conn, usedOldPassword, err := connectFallback(apiOpen, info, oldPassword)
if err != nil {
return nil, errors.Trace(err)
}
// ...and make sure we close it if anything goes wrong.
defer func() {
if err != nil {
if err := conn.Close(); err != nil {
logger.Errorf("while closing API connection: %v", err)
}
}
}()
// Update the agent config if necessary; this should just read the
// conn's properties, rather than making api calls, so we don't
// need to think about facades yet.
maybeSetAgentModelTag(a, conn)
// newConnFacade is patched out in export_test, because exhaustion.
// proper config/params struct would be better.
facade, err := newConnFacade(conn)
if err != nil {
return nil, errors.Trace(err)
}
// First of all, see if we're dead or removed, which will render
// any further work pointless.
entity := agentConfig.Tag()
life, err := facade.Life(entity)
if err != nil {
return nil, errors.Trace(err)
}
switch life {
case apiagent.Alive, apiagent.Dying:
case apiagent.Dead:
return nil, errAgentEntityDead
default:
return nil, errors.Errorf("unknown life value %q", life)
}
// If we need to change the password, it's far cleaner to
// exit with ErrChangedPassword and depend on the framework
// for expeditious retry than it is to mess around with those
// responsibilities in here.
if usedOldPassword {
logger.Debugf("changing password...")
err := changePassword(oldPassword, a, facade)
if err != nil {
return nil, errors.Trace(err)
}
logger.Debugf("password changed")
return nil, ErrChangedPassword
}
// If we *didn't* need to change the password, we apparently need
// to reset our password to its current value anyway. Reportedly,
// a machine agent promoted to controller status might have bad
// auth data in mongodb, and this "fixes" it... but this is scary,
// wrong, coincidental duct tape. The RTTD is to make controller-
// promotion work correctly in the first place.
//
// Still, can't fix everything at once.
//.........这里部分代码省略.........
示例14: Run
func (c *addModelCommand) Run(ctx *cmd.Context) error {
api, err := c.newAPIRoot()
if err != nil {
return errors.Annotate(err, "opening API connection")
}
defer api.Close()
store := c.ClientStore()
controllerName := c.ControllerName()
accountDetails, err := store.AccountDetails(controllerName)
if err != nil {
return errors.Trace(err)
}
modelOwner := accountDetails.User
if c.Owner != "" {
if !names.IsValidUser(c.Owner) {
return errors.Errorf("%q is not a valid user name", c.Owner)
}
modelOwner = names.NewUserTag(c.Owner).Id()
}
forUserSuffix := fmt.Sprintf(" for user '%s'", names.NewUserTag(modelOwner).Name())
attrs, err := c.getConfigValues(ctx)
if err != nil {
return errors.Trace(err)
}
cloudClient := c.newCloudAPI(api)
var cloudTag names.CloudTag
var cloud jujucloud.Cloud
var cloudRegion string
if c.CloudRegion != "" {
cloudTag, cloud, cloudRegion, err = c.getCloudRegion(cloudClient)
if err != nil {
return errors.Trace(err)
}
}
// If the user has specified a credential, then we will upload it if
// it doesn't already exist in the controller, and it exists locally.
var credentialTag names.CloudCredentialTag
if c.CredentialName != "" {
var err error
if c.CloudRegion == "" {
if cloudTag, cloud, err = defaultCloud(cloudClient); err != nil {
return errors.Trace(err)
}
}
credentialTag, err = c.maybeUploadCredential(ctx, cloudClient, cloudTag, cloudRegion, cloud, modelOwner)
if err != nil {
return errors.Trace(err)
}
}
addModelClient := c.newAddModelAPI(api)
model, err := addModelClient.CreateModel(c.Name, modelOwner, cloudTag.Id(), cloudRegion, credentialTag, attrs)
if err != nil {
if params.IsCodeUnauthorized(err) {
common.PermissionsMessage(ctx.Stderr, "add a model")
}
return errors.Trace(err)
}
messageFormat := "Added '%s' model"
messageArgs := []interface{}{c.Name}
if modelOwner == accountDetails.User {
controllerName := c.ControllerName()
if err := store.UpdateModel(controllerName, c.Name, jujuclient.ModelDetails{
model.UUID,
}); err != nil {
return errors.Trace(err)
}
if err := store.SetCurrentModel(controllerName, c.Name); err != nil {
return errors.Trace(err)
}
}
if c.CloudRegion != "" || model.CloudRegion != "" {
// The user explicitly requested a cloud/region,
// or the cloud supports multiple regions. Whichever
// the case, tell the user which cloud/region the
// model was deployed to.
cloudRegion := model.Cloud
if model.CloudRegion != "" {
cloudRegion += "/" + model.CloudRegion
}
messageFormat += " on %s"
messageArgs = append(messageArgs, cloudRegion)
}
if model.CloudCredential != "" {
tag := names.NewCloudCredentialTag(model.CloudCredential)
credentialName := tag.Name()
if tag.Owner().Id() != modelOwner {
credentialName = fmt.Sprintf("%s/%s", tag.Owner().Id(), credentialName)
}
messageFormat += " with credential '%s'"
messageArgs = append(messageArgs, credentialName)
}
//.........这里部分代码省略.........
示例15: openAPIState
// openAPIState opens the API using the given information, and
// returns the opened state and the api entity with
// the given tag. The given changeConfig function is
// called if the password changes to set the password.
func openAPIState(agentConfig agent.Config, a Agent) (_ *api.State, _ *apiagent.Entity, resultErr 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.
info := agentConfig.APIInfo()
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 = agentConfig.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) {
return nil, nil, worker.ErrTerminateAgent
}
if params.IsCodeUnauthorized(err) {
return nil, nil, worker.ErrTerminateAgent
}
return nil, nil, err
}
defer func() {
if resultErr != nil && st != nil {
st.Close()
}
}()
entity, err := st.Agent().Entity(a.Tag())
if err == nil && entity.Life() == params.Dead {
return nil, nil, worker.ErrTerminateAgent
}
if err != nil {
if params.IsCodeUnauthorized(err) {
return nil, nil, worker.ErrTerminateAgent
}
return nil, nil, err
}
if usedOldPassword {
// We succeeded in connecting with the fallback
// password, so we need to create a new password
// for the future.
newPassword, err := utils.RandomPassword()
if err != nil {
return nil, nil, err
}
// Change the configuration *before* setting the entity
// password, so that we avoid the possibility that
// we might successfully change the entity's
// password but fail to write the configuration,
// thus locking us out completely.
if err := a.ChangeConfig(func(c agent.ConfigSetter) error {
c.SetPassword(newPassword)
c.SetOldPassword(info.Password)
return nil
}); err != nil {
return nil, nil, err
}
if err := entity.SetPassword(newPassword); err != nil {
return nil, nil, err
}
st.Close()
info.Password = newPassword
st, err = apiOpen(info, api.DialOpts{})
if err != nil {
return nil, nil, err
}
}
return st, entity, nil
}