// newState returns a new State that uses the given environment. // The environment must have already been bootstrapped. func newState(environ environs.Environ, mongoInfo *mongo.MongoInfo) (*state.State, error) { config := environ.Config() password := config.AdminSecret() if password == "" { return nil, fmt.Errorf("cannot connect without admin-secret") } modelTag := names.NewModelTag(config.UUID()) mongoInfo.Password = password opts := mongo.DefaultDialOpts() st, err := state.Open(modelTag, mongoInfo, opts, environs.NewStatePolicy()) if errors.IsUnauthorized(errors.Cause(err)) { // We try for a while because we might succeed in // connecting to mongo before the state has been // initialized and the initial password set. for a := redialStrategy.Start(); a.Next(); { st, err = state.Open(modelTag, mongoInfo, opts, environs.NewStatePolicy()) if !errors.IsUnauthorized(errors.Cause(err)) { break } } if err != nil { return nil, err } } else if err != nil { return nil, err } if err := updateSecrets(environ, st); err != nil { st.Close() return nil, fmt.Errorf("unable to push secrets: %v", err) } return st, nil }
// getMaybeSignedMetadata returns metadata records matching the specified constraint in params. func getMaybeSignedMetadata(source DataSource, params GetMetadataParams, signed bool) ([]interface{}, *ResolveInfo, error) { makeIndexPath := func(basePath string) string { pathNoSuffix := fmt.Sprintf(basePath, params.StreamsVersion) indexPath := pathNoSuffix + UnsignedSuffix if signed { indexPath = pathNoSuffix + SignedSuffix } return indexPath } resolveInfo := &ResolveInfo{} resolveInfo.Source = source.Description() resolveInfo.Signed = signed indexPath := makeIndexPath(defaultIndexPath) logger.Tracef("looking for data index using path %s", indexPath) mirrorsPath := fmt.Sprintf(defaultMirrorsPath, params.StreamsVersion) cons := params.LookupConstraint indexRef, indexURL, err := fetchIndex( source, indexPath, mirrorsPath, cons.Params().CloudSpec, signed, params.ValueParams, ) logger.Tracef("looking for data index using URL %s", indexURL) if errors.IsNotFound(err) || errors.IsUnauthorized(err) { legacyIndexPath := makeIndexPath(defaultLegacyIndexPath) logger.Tracef("%s not accessed, actual error: %v", indexPath, err) logger.Tracef("%s not accessed, trying legacy index path: %s", indexPath, legacyIndexPath) indexPath = legacyIndexPath indexRef, indexURL, err = fetchIndex( source, indexPath, mirrorsPath, cons.Params().CloudSpec, signed, params.ValueParams, ) } resolveInfo.IndexURL = indexURL if err != nil { if errors.IsNotFound(err) || errors.IsUnauthorized(err) { logger.Tracef("cannot load index %q: %v", indexURL, err) } return nil, resolveInfo, err } logger.Tracef("read metadata index at %q", indexURL) items, err := indexRef.getLatestMetadataWithFormat(cons, ProductFormat, signed) if err != nil { if errors.IsNotFound(err) { logger.Debugf("skipping index %q because of missing information: %v", indexURL, err) return nil, resolveInfo, err } if _, ok := err.(*noMatchingProductsError); ok { logger.Debugf("%v", err) } } if indexRef.Source.Description() == "mirror" { resolveInfo.MirrorURL = indexRef.Source.(*urlDataSource).baseURL } return items, resolveInfo, err }
// NewConn returns a new Conn that uses the // given environment. The environment must have already // been bootstrapped. func NewConn(environ environs.Environ) (*Conn, error) { info, _, err := environ.StateInfo() if err != nil { return nil, err } password := environ.Config().AdminSecret() if password == "" { return nil, fmt.Errorf("cannot connect without admin-secret") } err = environs.CheckEnvironment(environ) if err != nil { return nil, err } info.Password = password opts := state.DefaultDialOpts() st, err := state.Open(info, opts, environs.NewStatePolicy()) if errors.IsUnauthorized(err) { logger.Infof("authorization error while connecting to state server; retrying") // We can't connect with the administrator password,; // perhaps this was the first connection and the // password has not been changed yet. info.Password = utils.UserPasswordHash(password, utils.CompatSalt) // We try for a while because we might succeed in // connecting to mongo before the state has been // initialized and the initial password set. for a := redialStrategy.Start(); a.Next(); { st, err = state.Open(info, opts, environs.NewStatePolicy()) if !errors.IsUnauthorized(err) { break } } if err != nil { return nil, err } if err := st.SetAdminMongoPassword(password); err != nil { return nil, err } } else if err != nil { return nil, err } conn := &Conn{ Environ: environ, State: st, } if err := conn.updateSecrets(); err != nil { conn.Close() return nil, fmt.Errorf("unable to push secrets: %v", err) } return conn, nil }
// ReleaseLease releases the lease held for namespace by id. func (m *leaseManager) ReleaseLease(namespace, id string) (err error) { ch := make(chan error, 1) token := Token{Namespace: namespace, Id: id} select { case <-m.tomb.Dying(): return errWorkerStopped case m.releaseLease <- releaseLeaseMsg{token, ch}: } select { case <-m.tomb.Dying(): return errWorkerStopped case err = <-ch: } if err != nil { err = errors.Annotatef(err, `could not release lease for namespace %q, id %q`, namespace, id) // Log errors so that we're aware they're happening, but don't // burden the caller with dealing with an error if it's // essential a no-op. if errors.IsUnauthorized(err) { logger.Warningf(err.Error()) return nil } return err } return nil }
// ServerError returns an error suitable for returning to an API // client, with an error code suitable for various kinds of errors // generated in packages outside the API. func ServerError(err error) *params.Error { if err == nil { return nil } code, ok := singletonCode(err) switch { case ok: case errors.IsUnauthorized(err): code = params.CodeUnauthorized case errors.IsNotFound(err): code = params.CodeNotFound case errors.IsAlreadyExists(err): code = params.CodeAlreadyExists case state.IsNotAssigned(err): code = params.CodeNotAssigned case state.IsHasAssignedUnitsError(err): code = params.CodeHasAssignedUnits case IsNoAddressSetError(err): code = params.CodeNoAddressSet case state.IsNotProvisionedError(err): code = params.CodeNotProvisioned case IsUnknownEnviromentError(err): code = params.CodeNotFound default: code = params.ErrCode(err) } return ¶ms.Error{ Message: err.Error(), Code: code, } }
// ServerError returns an error suitable for returning to an API // client, with an error code suitable for various kinds of errors // generated in packages outside the API. func ServerError(err error) *params.Error { if err == nil { return nil } logger.Tracef("server RPC error %v", errors.Details(err)) msg := err.Error() // Skip past annotations when looking for the code. err = errors.Cause(err) code, ok := singletonCode(err) var info *params.ErrorInfo switch { case ok: case errors.IsUnauthorized(err): code = params.CodeUnauthorized case errors.IsNotFound(err): code = params.CodeNotFound case errors.IsUserNotFound(err): code = params.CodeUserNotFound case errors.IsAlreadyExists(err): code = params.CodeAlreadyExists case errors.IsNotAssigned(err): code = params.CodeNotAssigned case state.IsHasAssignedUnitsError(err): code = params.CodeHasAssignedUnits case state.IsHasHostedModelsError(err): code = params.CodeHasHostedModels case isNoAddressSetError(err): code = params.CodeNoAddressSet case errors.IsNotProvisioned(err): code = params.CodeNotProvisioned case IsUpgradeInProgressError(err): code = params.CodeUpgradeInProgress case state.IsHasAttachmentsError(err): code = params.CodeMachineHasAttachedStorage case isUnknownModelError(err): code = params.CodeModelNotFound case errors.IsNotSupported(err): code = params.CodeNotSupported case errors.IsBadRequest(err): code = params.CodeBadRequest case errors.IsMethodNotAllowed(err): code = params.CodeMethodNotAllowed default: if err, ok := err.(*DischargeRequiredError); ok { code = params.CodeDischargeRequired info = ¶ms.ErrorInfo{ Macaroon: err.Macaroon, // One macaroon fits all. MacaroonPath: "/", } break } code = params.ErrCode(err) } return ¶ms.Error{ Message: msg, Code: code, Info: info, } }
// GetIndexWithFormat returns a simplestreams index of the specified format. // Exported for testing. func GetIndexWithFormat(source DataSource, indexPath, indexFormat, mirrorsPath string, requireSigned bool, cloudSpec CloudSpec, params ValueParams) (*IndexReference, error) { data, url, err := fetchData(source, indexPath, requireSigned) if err != nil { if errors.IsNotFound(err) || errors.IsUnauthorized(err) { return nil, err } return nil, fmt.Errorf("cannot read index data, %v", err) } var indices Indices err = json.Unmarshal(data, &indices) if err != nil { logger.Errorf("bad JSON index data at URL %q: %v", url, string(data)) return nil, fmt.Errorf("cannot unmarshal JSON index metadata at URL %q: %v", url, err) } if indices.Format != indexFormat { return nil, fmt.Errorf( "unexpected index file format %q, expected %q at URL %q", indices.Format, indexFormat, url) } mirrors, url, err := getMirrorRefs(source, mirrorsPath, requireSigned, params) if err != nil && !errors.IsNotFound(err) && !errors.IsUnauthorized(err) { return nil, fmt.Errorf("cannot load mirror metadata at URL %q: %v", url, err) } indexRef := &IndexReference{ Source: source, Indices: indices, valueParams: params, } // Apply any mirror information to the source. if params.MirrorContentId != "" { mirrorInfo, err := getMirror( source, mirrors, params.DataType, params.MirrorContentId, cloudSpec, requireSigned) if err == nil { logger.Debugf("using mirrored products path: %s", path.Join(mirrorInfo.MirrorURL, mirrorInfo.Path)) indexRef.Source = NewURLSignedDataSource("mirror", mirrorInfo.MirrorURL, source.PublicSigningKey(), utils.VerifySSLHostnames, source.Priority(), requireSigned) indexRef.MirroredProductsPath = mirrorInfo.Path } else { logger.Tracef("no mirror information available for %s: %v", cloudSpec, err) } } return indexRef, nil }
// newState returns a new State that uses the given environment. // The environment must have already been bootstrapped. func newState(environ environs.Environ, mongoInfo *authentication.MongoInfo) (*state.State, error) { password := environ.Config().AdminSecret() if password == "" { return nil, fmt.Errorf("cannot connect without admin-secret") } if err := environs.CheckEnvironment(environ); err != nil { return nil, err } mongoInfo.Password = password opts := mongo.DefaultDialOpts() st, err := state.Open(mongoInfo, opts, environs.NewStatePolicy()) if errors.IsUnauthorized(err) { // We can't connect with the administrator password,; // perhaps this was the first connection and the // password has not been changed yet. mongoInfo.Password = utils.UserPasswordHash(password, utils.CompatSalt) // We try for a while because we might succeed in // connecting to mongo before the state has been // initialized and the initial password set. for a := redialStrategy.Start(); a.Next(); { st, err = state.Open(mongoInfo, opts, environs.NewStatePolicy()) if !errors.IsUnauthorized(err) { break } } if err != nil { return nil, err } if err := st.SetAdminMongoPassword(password); err != nil { return nil, err } } else if err != nil { return nil, err } if err := updateSecrets(environ, st); err != nil { st.Close() return nil, fmt.Errorf("unable to push secrets: %v", err) } return st, nil }
// newState returns a new State that uses the given environment. // The environment must have already been bootstrapped. func newState(controllerUUID string, environ environs.Environ, mongoInfo *mongo.MongoInfo) (*state.State, error) { if controllerUUID == "" { return nil, errors.New("missing controller UUID") } config := environ.Config() password := AdminSecret if password == "" { return nil, errors.Errorf("cannot connect without admin-secret") } modelTag := names.NewModelTag(config.UUID()) mongoInfo.Password = password opts := mongotest.DialOpts() newPolicyFunc := stateenvirons.GetNewPolicyFunc( stateenvirons.GetNewEnvironFunc(environs.New), ) controllerTag := names.NewControllerTag(controllerUUID) st, err := state.Open(modelTag, controllerTag, mongoInfo, opts, newPolicyFunc) if errors.IsUnauthorized(errors.Cause(err)) { // We try for a while because we might succeed in // connecting to mongo before the state has been // initialized and the initial password set. for a := redialStrategy.Start(); a.Next(); { st, err = state.Open(modelTag, controllerTag, mongoInfo, opts, newPolicyFunc) if !errors.IsUnauthorized(errors.Cause(err)) { break } } if err != nil { return nil, err } } else if err != nil { return nil, err } return st, nil }
// GetMirrorMetadataWithFormat returns simplestreams mirror data of the specified format. // Exported for testing. func GetMirrorMetadataWithFormat(source DataSource, mirrorPath, format string, requireSigned bool) (*MirrorMetadata, error) { data, url, err := fetchData(source, mirrorPath, requireSigned) if err != nil { if errors.IsNotFound(err) || errors.IsUnauthorized(err) { return nil, err } return nil, fmt.Errorf("cannot read mirror data, %v", err) } var mirrors MirrorMetadata err = json.Unmarshal(data, &mirrors) if err != nil { return nil, fmt.Errorf("cannot unmarshal JSON mirror metadata at URL %q: %v", url, err) } if mirrors.Format != format { return nil, fmt.Errorf("unexpected mirror file format %q, expected %q at URL %q", mirrors.Format, format, url) } return &mirrors, nil }
// getMaybeSignedMetadata returns metadata records matching the specified constraint. func getMaybeSignedMetadata(source DataSource, baseIndexPath string, cons LookupConstraint, signed bool, params ValueParams) ([]interface{}, *ResolveInfo, error) { resolveInfo := &ResolveInfo{} indexPath := baseIndexPath + UnsignedSuffix if signed { indexPath = baseIndexPath + signedSuffix } var items []interface{} indexURL, err := source.URL(indexPath) if err != nil { // Some providers return an error if asked for the URL of a non-existent file. // So the best we can do is use the relative path for the URL when logging messages. indexURL = indexPath } resolveInfo.Source = source.Description() resolveInfo.Signed = signed resolveInfo.IndexURL = indexURL indexRef, err := GetIndexWithFormat(source, indexPath, "index:1.0", signed, cons.Params().CloudSpec, params) if err != nil { if errors.IsNotFound(err) || errors.IsUnauthorized(err) { logger.Debugf("cannot load index %q: %v", indexURL, err) } return nil, resolveInfo, err } logger.Debugf("read metadata index at %q", indexURL) items, err = indexRef.getLatestMetadataWithFormat(cons, "products:1.0", signed) if err != nil { if errors.IsNotFound(err) { logger.Debugf("skipping index because of error getting latest metadata %q: %v", indexURL, err) return nil, resolveInfo, err } if _, ok := err.(*noMatchingProductsError); ok { logger.Debugf("%v", err) } } if indexRef.Source.Description() == "mirror" { resolveInfo.MirrorURL = indexRef.Source.(*urlDataSource).baseURL } return items, resolveInfo, err }
// getMirrorRefs parses and returns a simplestreams mirror reference. func getMirrorRefs(source DataSource, baseMirrorsPath string, requireSigned bool, params ValueParams) (MirrorRefs, string, error) { mirrorsPath := baseMirrorsPath + UnsignedSuffix if requireSigned { mirrorsPath = baseMirrorsPath + SignedSuffix } var mirrors MirrorRefs data, url, err := fetchData(source, mirrorsPath, requireSigned) if err != nil { if errors.IsNotFound(err) || errors.IsUnauthorized(err) { return mirrors, url, err } return mirrors, url, fmt.Errorf("cannot read mirrors data, %v", err) } err = json.Unmarshal(data, &mirrors) if err != nil { return mirrors, url, fmt.Errorf("cannot unmarshal JSON mirror metadata at URL %q: %v", url, err) } return mirrors, url, err }
// ServerError returns an error suitable for returning to an API // client, with an error code suitable for various kinds of errors // generated in packages outside the API. func ServerError(err error) *params.Error { if err == nil { return nil } msg := err.Error() // Skip past annotations when looking for the code. err = errors.Cause(err) code, ok := singletonCode(err) switch { case ok: case errors.IsUnauthorized(err): code = params.CodeUnauthorized case errors.IsNotFound(err): code = params.CodeNotFound case errors.IsAlreadyExists(err): code = params.CodeAlreadyExists case errors.IsNotAssigned(err): code = params.CodeNotAssigned case state.IsHasAssignedUnitsError(err): code = params.CodeHasAssignedUnits case IsNoAddressSetError(err): code = params.CodeNoAddressSet case errors.IsNotProvisioned(err): code = params.CodeNotProvisioned case state.IsUpgradeInProgressError(err): code = params.CodeUpgradeInProgress case state.IsHasAttachmentsError(err): code = params.CodeMachineHasAttachedStorage case IsUnknownEnviromentError(err): code = params.CodeNotFound case errors.IsNotSupported(err): code = params.CodeNotSupported default: code = params.ErrCode(err) } return ¶ms.Error{ Message: msg, Code: code, } }