func (env *Environment) loadSchemas() error { schemaValue, err := env.VM.Get(schemasVar) if err != nil { return fmt.Errorf("%s string array not specified", schemasVar) } schemaFilenames, err := gohan_otto.GetStringList(schemaValue) if err != nil { return fmt.Errorf("Bad type of %s - expected an array of strings", schemasVar) } manager := schema.GetManager() for _, schema := range schemaFilenames { err = manager.LoadSchemaFromFile(schema) if err != nil { return err } } environmentManager := extension.GetManager() for schemaID := range manager.Schemas() { environmentManager.RegisterEnvironment(schemaID, env) } pathValue, err := env.VM.Get(pathVar) if err != nil || !pathValue.IsString() { return fmt.Errorf("%s string not specified", pathVar) } pathString, _ := pathValue.ToString() return env.LoadExtensionsForPath(manager.Extensions, pathString) }
//GetResourcesInTransaction returns specified resources without calling non in_transaction events func GetResourcesInTransaction(context middleware.Context, resourceSchema *schema.Schema, filter map[string]interface{}, paginator *pagination.Paginator) error { mainTransaction := context["transaction"].(transaction.Transaction) response := map[string]interface{}{} environmentManager := extension.GetManager() environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) if !ok { return fmt.Errorf("no environment for schema") } if err := extension.HandleEvent(context, environment, "pre_list_in_transaction"); err != nil { return err } list, total, err := mainTransaction.List(resourceSchema, filter, paginator) if err != nil { response[resourceSchema.Plural] = []interface{}{} context["response"] = response return err } data := []interface{}{} for _, resource := range list { data = append(data, resource.Data()) } response[resourceSchema.Plural] = data context["response"] = response context["total"] = total if err := extension.HandleEvent(context, environment, "post_list_in_transaction"); err != nil { return err } return nil }
//GetSingleResourceInTransaction get resource in single transaction func GetSingleResourceInTransaction(context middleware.Context, resourceSchema *schema.Schema, resourceID string, tenantIDs []string) (err error) { mainTransaction := context["transaction"].(transaction.Transaction) environmentManager := extension.GetManager() environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) if !ok { return fmt.Errorf("no environment for schema") } if err := extension.HandleEvent(context, environment, "pre_show_in_transaction"); err != nil { return err } if rawResponse, ok := context["response"]; ok { if _, ok := rawResponse.(map[string]interface{}); ok { return nil } return fmt.Errorf("extension returned invalid JSON: %v", rawResponse) } object, err := mainTransaction.Fetch(resourceSchema, resourceID, tenantIDs) if err != nil || object == nil { return ResourceError{err, "", NotFound} } response := map[string]interface{}{} response[resourceSchema.Singular] = object.Data() context["response"] = response if err := extension.HandleEvent(context, environment, "post_show_in_transaction"); err != nil { return err } return }
//CreateResourceInTransaction craete db resource model in transaction func CreateResourceInTransaction(context middleware.Context, resource *schema.Resource) error { resourceSchema := resource.Schema() mainTransaction := context["transaction"].(transaction.Transaction) environmentManager := extension.GetManager() environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) if !ok { return fmt.Errorf("No environment for schema") } if err := extension.HandleEvent(context, environment, "pre_create_in_transaction"); err != nil { return err } if err := mainTransaction.Create(resource); err != nil { log.Debug("%s transaction error", err) return ResourceError{ err, fmt.Sprintf("Failed to store data in database: %v", err), CreateFailed} } response := map[string]interface{}{} response[resourceSchema.Singular] = resource.Data() context["response"] = response if err := extension.HandleEvent(context, environment, "post_create_in_transaction"); err != nil { return err } return nil }
// ActionResource runs custom action on resource func ActionResource(context middleware.Context, dataStore db.DB, identityService middleware.IdentityService, resourceSchema *schema.Schema, action schema.Action, resourceID string, data interface{}, ) error { actionSchema := action.InputSchema context["input"] = data context["id"] = resourceID environmentManager := extension.GetManager() environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) if !ok { return fmt.Errorf("No environment for schema") } if actionSchema != nil { err := resourceSchema.Validate(actionSchema, data) if err != nil { return ResourceError{err, fmt.Sprintf("Validation error: %s", err), WrongData} } } err := extension.HandleEvent(context, environment, action.ID) if err != nil { return err } if _, ok := context["response"]; ok { return nil } return fmt.Errorf("no response") }
// UpdateResourceInTransaction updates resource in db in transaction func UpdateResourceInTransaction( context middleware.Context, resourceSchema *schema.Schema, resourceID string, dataMap map[string]interface{}, tenantIDs []string) error { manager := schema.GetManager() mainTransaction := context["transaction"].(transaction.Transaction) environmentManager := extension.GetManager() environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) if !ok { return fmt.Errorf("No environment for schema") } resource, err := mainTransaction.Fetch( resourceSchema, resourceID, tenantIDs) if err != nil { return ResourceError{err, err.Error(), WrongQuery} } policy := context["policy"].(*schema.Policy) // apply property filter err = policy.ApplyPropertyConditionFilter(schema.ActionUpdate, resource.Data(), dataMap) if err != nil { return ResourceError{err, "", Unauthorized} } err = resource.Update(dataMap) if err != nil { return ResourceError{err, err.Error(), WrongData} } context["resource"] = resource.Data() if err := extension.HandleEvent(context, environment, "pre_update_in_transaction"); err != nil { return err } dataMap, ok = context["resource"].(map[string]interface{}) if !ok { return fmt.Errorf("Resource not JSON: %s", err) } resource, err = manager.LoadResource(resourceSchema.ID, dataMap) if err != nil { return fmt.Errorf("Loading Resource failed: %s", err) } err = mainTransaction.Update(resource) if err != nil { return ResourceError{err, fmt.Sprintf("Failed to store data in database: %v", err), UpdateFailed} } response := map[string]interface{}{} response[resourceSchema.Singular] = resource.Data() context["response"] = response if err := extension.HandleEvent(context, environment, "post_update_in_transaction"); err != nil { return err } return nil }
func (env *Environment) registerEnvironments() error { manager := schema.GetManager() environmentManager := extension.GetManager() for schemaID := range manager.Schemas() { // Note: the following code ignores errors related to registration // of an environment that has already been registered environmentManager.RegisterEnvironment(schemaID, env) } return nil }
// DeleteResource deletes the resource specified by the schema and ID func DeleteResource(context middleware.Context, dataStore db.DB, resourceSchema *schema.Schema, resourceID string, ) error { context["id"] = resourceID environmentManager := extension.GetManager() environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) if !ok { return fmt.Errorf("No environment for schema") } auth := context["auth"].(schema.Authorization) policy, err := loadPolicy(context, "delete", strings.Replace(resourceSchema.GetSingleURL(), ":id", resourceID, 1), auth) if err != nil { return err } context["policy"] = policy preTransaction, err := dataStore.Begin() if err != nil { return fmt.Errorf("cannot create transaction: %v", err) } tenantIDs := policy.GetTenantIDFilter(schema.ActionDelete, auth.TenantID()) filter := transaction.IDFilter(resourceID) if tenantIDs != nil { filter["tenant_id"] = tenantIDs } resource, fetchErr := preTransaction.Fetch(resourceSchema, filter) preTransaction.Close() if resource != nil { context["resource"] = resource.Data() } if err := extension.HandleEvent(context, environment, "pre_delete"); err != nil { return err } if fetchErr != nil { return ResourceError{err, "", NotFound} } if err := InTransaction( context, dataStore, transaction.GetIsolationLevel(resourceSchema, schema.ActionDelete), func() error { return DeleteResourceInTransaction(context, resourceSchema, resourceID) }, ); err != nil { return err } if err := extension.HandleEvent(context, environment, "post_delete"); err != nil { return err } return nil }
//GetResourcesInTransaction returns specified resources without calling non in_transaction events func GetResourcesInTransaction(context middleware.Context, resourceSchema *schema.Schema, filter map[string]interface{}, paginator *pagination.Paginator) error { mainTransaction := context["transaction"].(transaction.Transaction) auth := context["auth"].(schema.Authorization) response := map[string]interface{}{} environmentManager := extension.GetManager() environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) if !ok { return fmt.Errorf("no environment for schema") } if err := handleEvent(context, environment, "pre_list_in_transaction"); err != nil { return err } var err error var total uint64 list := []*schema.Resource{} if resourceSchema.ID == "schema" { manager := schema.GetManager() for _, currentSchema := range manager.OrderedSchemas() { trimmedSchema, err := GetSchema(currentSchema, auth) if err != nil { return err } if trimmedSchema != nil { list = append(list, trimmedSchema) total = total + 1 } } } else { list, total, err = mainTransaction.List(resourceSchema, filter, paginator) if err != nil { response[resourceSchema.Plural] = []interface{}{} context["response"] = response return err } } data := []interface{}{} for _, resource := range list { data = append(data, resource.Data()) } response[resourceSchema.Plural] = data context["response"] = response context["total"] = total if err := handleEvent(context, environment, "post_list_in_transaction"); err != nil { return err } return nil }
// ActionResource runs custom action on resource func ActionResource(context middleware.Context, dataStore db.DB, identityService middleware.IdentityService, resourceSchema *schema.Schema, action schema.Action, resourceID string, data interface{}, ) error { actionSchema := action.InputSchema context["input"] = data context["id"] = resourceID environmentManager := extension.GetManager() environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) if !ok { return fmt.Errorf("No environment for schema") } err := resourceSchema.Validate(actionSchema, data) if err != nil { return ResourceError{err, fmt.Sprintf("Validation error: %s", err), WrongData} } if err := handleEvent(context, environment, fmt.Sprintf("pre_%s", action.ID)); err != nil { return err } if err := InTransaction(context, dataStore, func() error { return handleEvent(context, environment, fmt.Sprintf("pre_%s_in_transaction", action.ID)) }); err != nil { return err } if err := handleEvent(context, environment, action.ID); err != nil { return err } if err := InTransaction(context, dataStore, func() error { return handleEvent(context, environment, fmt.Sprintf("post_%s_in_transaction", action.ID)) }); err != nil { return err } if err := handleEvent(context, environment, fmt.Sprintf("post_%s", action.ID)); err != nil { return err } if rawResponse, ok := context["response"]; ok { if _, ok := rawResponse.(map[string]interface{}); ok { return nil } return fmt.Errorf("extension returned invalid JSON: %v", rawResponse) } return fmt.Errorf("no response") }
// GetMultipleResources returns all resources specified by the schema and query parameters func GetMultipleResources(context middleware.Context, dataStore db.DB, resourceSchema *schema.Schema, queryParameters map[string][]string) error { log.Debug("Start get multiple resources!!") auth := context["auth"].(schema.Authorization) policy, err := loadPolicy(context, "read", resourceSchema.GetPluralURL(), auth) if err != nil { return err } filter := FilterFromQueryParameter(resourceSchema, queryParameters) if policy.RequireOwner() { filter["tenant_id"] = policy.GetTenantIDFilter(schema.ActionRead, auth.TenantID()) } filter = policy.RemoveHiddenProperty(filter) paginator, err := pagination.FromURLQuery(resourceSchema, queryParameters) if err != nil { return ResourceError{err, err.Error(), WrongQuery} } context["policy"] = policy environmentManager := extension.GetManager() environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) if !ok { return fmt.Errorf("No environment for schema") } if err := extension.HandleEvent(context, environment, "pre_list"); err != nil { return err } if rawResponse, ok := context["response"]; ok { if _, ok := rawResponse.(map[string]interface{}); ok { return nil } return fmt.Errorf("extension returned invalid JSON: %v", rawResponse) } if err := GetResources(context, dataStore, resourceSchema, filter, paginator); err != nil { return err } if err := extension.HandleEvent(context, environment, "post_list"); err != nil { return err } if err := ApplyPolicyForResources(context, resourceSchema); err != nil { return err } return nil }
// GetSingleResource returns the resource specified by the schema and ID func GetSingleResource(context middleware.Context, dataStore db.DB, resourceSchema *schema.Schema, resourceID string) error { context["id"] = resourceID auth := context["auth"].(schema.Authorization) policy, err := loadPolicy(context, "read", strings.Replace(resourceSchema.GetSingleURL(), ":id", resourceID, 1), auth) if err != nil { return err } context["policy"] = policy environmentManager := extension.GetManager() environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) if !ok { return fmt.Errorf("No environment for schema") } if err := extension.HandleEvent(context, environment, "pre_show"); err != nil { return err } if rawResponse, ok := context["response"]; ok { if _, ok := rawResponse.(map[string]interface{}); ok { return nil } return fmt.Errorf("extension returned invalid JSON: %v", rawResponse) } if err := InTransaction( context, dataStore, transaction.GetIsolationLevel(resourceSchema, schema.ActionRead), func() error { return GetSingleResourceInTransaction(context, resourceSchema, resourceID, policy.GetTenantIDFilter(schema.ActionRead, auth.TenantID())) }, ); err != nil { return err } if err := extension.HandleEvent(context, environment, "post_show"); err != nil { return err } if err := ApplyPolicyForResource(context, resourceSchema); err != nil { return ResourceError{err, "", NotFound} } return nil }
//DeleteResourceInTransaction deletes resources in a transaction func DeleteResourceInTransaction(context middleware.Context, resourceSchema *schema.Schema, resourceID string) error { mainTransaction := context["transaction"].(transaction.Transaction) environmentManager := extension.GetManager() environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) if !ok { return fmt.Errorf("No environment for schema") } auth := context["auth"].(schema.Authorization) policy := context["policy"].(*schema.Policy) tenantIDs := policy.GetTenantIDFilter(schema.ActionDelete, auth.TenantID()) filter := transaction.IDFilter(resourceID) if tenantIDs != nil { filter["tenant_id"] = tenantIDs } resource, err := mainTransaction.Fetch(resourceSchema, filter) log.Debug("%s %s", resource, err) if err != nil { return err } if resource != nil { context["resource"] = resource.Data() } // apply property filter err = policy.ApplyPropertyConditionFilter(schema.ActionUpdate, resource.Data(), nil) if err != nil { return ResourceError{err, "", Unauthorized} } if err := extension.HandleEvent(context, environment, "pre_delete_in_transaction"); err != nil { return err } err = mainTransaction.Delete(resourceSchema, resourceID) if err != nil { return ResourceError{err, "", DeleteFailed} } if err := extension.HandleEvent(context, environment, "post_delete_in_transaction"); err != nil { return err } return nil }
//DeleteResourceInTransaction deletes resources in a transaction func DeleteResourceInTransaction(context middleware.Context, resourceSchema *schema.Schema, resourceID string) error { mainTransaction := context["transaction"].(transaction.Transaction) environmentManager := extension.GetManager() environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) if !ok { return fmt.Errorf("No environment for schema") } if err := extension.HandleEvent(context, environment, "pre_delete_in_transaction"); err != nil { return err } err := mainTransaction.Delete(resourceSchema, resourceID) if err != nil { return ResourceError{err, "", DeleteFailed} } if err := extension.HandleEvent(context, environment, "post_delete_in_transaction"); err != nil { return err } return nil }
// UpdateResource updates the resource specified by the schema and ID using the dataMap func UpdateResource( context middleware.Context, dataStore db.DB, identityService middleware.IdentityService, resourceSchema *schema.Schema, resourceID string, dataMap map[string]interface{}, ) error { context["id"] = resourceID //load environment environmentManager := extension.GetManager() environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) if !ok { return fmt.Errorf("No environment for schema") } auth := context["auth"].(schema.Authorization) //load policy policy, err := loadPolicy(context, "update", strings.Replace(resourceSchema.GetSingleURL(), ":id", resourceID, 1), auth) if err != nil { return err } context["policy"] = policy //fillup default values if tenantID, ok := dataMap["tenant_id"]; ok && tenantID != nil { dataMap["tenant_name"], err = identityService.GetTenantName(tenantID.(string)) } if err != nil { return ResourceError{err, err.Error(), Unauthorized} } //check policy err = policy.Check(schema.ActionUpdate, auth, dataMap) delete(dataMap, "tenant_name") if err != nil { return ResourceError{err, err.Error(), Unauthorized} } context["resource"] = dataMap if err := extension.HandleEvent(context, environment, "pre_update"); err != nil { return err } if resourceData, ok := context["resource"].(map[string]interface{}); ok { dataMap = resourceData } if err := InTransaction( context, dataStore, transaction.GetIsolationLevel(resourceSchema, schema.ActionUpdate), func() error { return UpdateResourceInTransaction(context, resourceSchema, resourceID, dataMap, policy.GetTenantIDFilter(schema.ActionUpdate, auth.TenantID())) }, ); err != nil { return err } if err := extension.HandleEvent(context, environment, "post_update"); err != nil { return err } if err := ApplyPolicyForResource(context, resourceSchema); err != nil { return ResourceError{err, "", NotFound} } return nil }
//StateUpdate updates the state in the db based on the sync event func StateUpdate(response *gohan_sync.Event, server *Server) error { lockKey := lockPath + response.Key err := server.sync.Lock(lockKey, false) if err != nil { return err } defer func() { server.sync.Unlock(lockKey) }() dataStore := server.db schemaPath := "/" + strings.TrimPrefix(response.Key, statePrefix) var curSchema *schema.Schema manager := schema.GetManager() for _, s := range manager.Schemas() { if strings.HasPrefix(schemaPath, s.URL) { curSchema = s break } } if curSchema == nil || !curSchema.StateVersioning() { log.Debug("State update on unexpected path '%s'", schemaPath) return nil } resourceID := strings.TrimPrefix(schemaPath, curSchema.URL+"/") tx, err := dataStore.Begin() if err != nil { return err } defer tx.Close() curResource, err := tx.Fetch(curSchema, resourceID, nil) if err != nil { return err } resourceState, err := tx.StateFetch(curSchema, resourceID, nil) if err != nil { return err } if resourceState.StateVersion == resourceState.ConfigVersion { return nil } stateVersion, ok := response.Data["version"].(float64) if !ok { return fmt.Errorf("No version in state information") } oldStateVersion := resourceState.StateVersion resourceState.StateVersion = int64(stateVersion) if resourceState.StateVersion < oldStateVersion { return nil } if newError, ok := response.Data["error"].(string); ok { resourceState.Error = newError } if newState, ok := response.Data["state"].(string); ok { resourceState.State = newState } environmentManager := extension.GetManager() environment, haveEnvironment := environmentManager.GetEnvironment(curSchema.ID) context := map[string]interface{}{} if haveEnvironment { serviceAuthorization, _ := server.keystoneIdentity.GetServiceAuthorization() context["catalog"] = serviceAuthorization.Catalog() context["auth_token"] = serviceAuthorization.AuthToken() context["resource"] = curResource.Data() context["state"] = response.Data context["config_version"] = resourceState.ConfigVersion context["transaction"] = tx if err := extension.HandleEvent(context, environment, "pre_state_update_in_transaction"); err != nil { return err } } err = tx.StateUpdate(curResource, &resourceState) if err != nil { return err } if haveEnvironment { if err := extension.HandleEvent(context, environment, "post_state_update_in_transaction"); err != nil { return err } } return tx.Commit() }
// CreateResource creates the resource specified by the schema and dataMap func CreateResource( context middleware.Context, dataStore db.DB, identityService middleware.IdentityService, resourceSchema *schema.Schema, dataMap map[string]interface{}, ) error { manager := schema.GetManager() // Load environment environmentManager := extension.GetManager() environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) if !ok { return fmt.Errorf("No environment for schema") } auth := context["auth"].(schema.Authorization) //LoadPolicy policy, err := loadPolicy(context, "create", resourceSchema.GetPluralURL(), auth) if err != nil { return err } _, err = resourceSchema.GetPropertyByID("tenant_id") if _, ok := dataMap["tenant_id"]; err == nil && !ok { dataMap["tenant_id"] = context["tenant_id"] } if tenantID, ok := dataMap["tenant_id"]; ok && tenantID != nil { dataMap["tenant_name"], err = identityService.GetTenantName(tenantID.(string)) if err != nil { return ResourceError{err, err.Error(), Unauthorized} } } //Apply policy for api input err = policy.Check(schema.ActionCreate, auth, dataMap) if err != nil { return ResourceError{err, err.Error(), Unauthorized} } delete(dataMap, "tenant_name") // apply property filter err = policy.ApplyPropertyConditionFilter(schema.ActionCreate, dataMap, nil) if err != nil { return ResourceError{err, err.Error(), Unauthorized} } context["resource"] = dataMap if id, ok := dataMap["id"]; !ok || id == "" { dataMap["id"] = uuid.NewV4().String() } context["id"] = dataMap["id"] if err := extension.HandleEvent(context, environment, "pre_create"); err != nil { return err } if resourceData, ok := context["resource"].(map[string]interface{}); ok { dataMap = resourceData } //Validation err = resourceSchema.ValidateOnCreate(dataMap) if err != nil { return ResourceError{err, fmt.Sprintf("Validation error: %s", err), WrongData} } resource, err := manager.LoadResource(resourceSchema.ID, dataMap) if err != nil { return err } //Fillup default err = resource.PopulateDefaults() if err != nil { return err } context["resource"] = resource.Data() if err := InTransaction( context, dataStore, transaction.GetIsolationLevel(resourceSchema, schema.ActionCreate), func() error { return CreateResourceInTransaction(context, resource) }, ); err != nil { return err } if err := extension.HandleEvent(context, environment, "post_create"); err != nil { return err } if err := ApplyPolicyForResource(context, resourceSchema); err != nil { return ResourceError{err, "", Unauthorized} } return nil }
//StateUpdate updates the state in the db based on the sync event func StateUpdate(response *gohan_sync.Event, server *Server) error { dataStore := server.db schemaPath := "/" + strings.TrimPrefix(response.Key, statePrefix) var curSchema = schema.GetSchemaByPath(schemaPath) if curSchema == nil || !curSchema.StateVersioning() { log.Debug("State update on unexpected path '%s'", schemaPath) return nil } resourceID := curSchema.GetResourceIDFromPath(schemaPath) tx, err := dataStore.Begin() if err != nil { return err } defer tx.Close() err = tx.SetIsolationLevel(transaction.GetIsolationLevel(curSchema, StateUpdateEventName)) if err != nil { return err } curResource, err := tx.Fetch(curSchema, transaction.IDFilter(resourceID)) if err != nil { return err } resourceState, err := tx.StateFetch(curSchema, transaction.IDFilter(resourceID)) if err != nil { return err } if resourceState.StateVersion == resourceState.ConfigVersion { return nil } stateVersion, ok := response.Data["version"].(float64) if !ok { return fmt.Errorf("No version in state information") } oldStateVersion := resourceState.StateVersion resourceState.StateVersion = int64(stateVersion) if resourceState.StateVersion < oldStateVersion { return nil } if newError, ok := response.Data["error"].(string); ok { resourceState.Error = newError } if newState, ok := response.Data["state"].(string); ok { resourceState.State = newState } environmentManager := extension.GetManager() environment, haveEnvironment := environmentManager.GetEnvironment(curSchema.ID) context := map[string]interface{}{} if haveEnvironment { serviceAuthorization, _ := server.keystoneIdentity.GetServiceAuthorization() context["catalog"] = serviceAuthorization.Catalog() context["auth_token"] = serviceAuthorization.AuthToken() context["resource"] = curResource.Data() context["schema"] = curSchema context["state"] = response.Data context["config_version"] = resourceState.ConfigVersion context["transaction"] = tx if err := extension.HandleEvent(context, environment, "pre_state_update_in_transaction"); err != nil { return err } } err = tx.StateUpdate(curResource, &resourceState) if err != nil { return err } if haveEnvironment { if err := extension.HandleEvent(context, environment, "post_state_update_in_transaction"); err != nil { return err } } return tx.Commit() }
schemaID1 = "Wormtongue" schemaID2 = "Dumbledore" ) var ( env1 extension.Environment env2 extension.Environment manager *extension.Manager ) BeforeEach(func() { env1 = otto.NewEnvironment(testDB1, &middleware.FakeIdentity{}) env2 = otto.NewEnvironment(testDB2, &middleware.FakeIdentity{}) }) JustBeforeEach(func() { manager = extension.GetManager() Expect(manager.RegisterEnvironment(schemaID1, env1)).To(Succeed()) }) AfterEach(func() { extension.ClearManager() }) Describe("Registering environments", func() { Context("When it isn't registered", func() { It("Should register it", func() { Expect(manager.RegisterEnvironment(schemaID2, env2)).To(Succeed()) }) }) Context("When it is registered", func() {
//MonitoringUpdate updates the state in the db based on the sync event func MonitoringUpdate(response *gohan_sync.Event, server *Server) error { dataStore := server.db schemaPath := "/" + strings.TrimPrefix(response.Key, monitoringPrefix) var curSchema = schema.GetSchemaByPath(schemaPath) if curSchema == nil || !curSchema.StateVersioning() { log.Debug("Monitoring update on unexpected path '%s'", schemaPath) return nil } resourceID := curSchema.GetResourceIDFromPath(schemaPath) tx, err := dataStore.Begin() if err != nil { return err } defer tx.Close() err = tx.SetIsolationLevel(transaction.GetIsolationLevel(curSchema, MonitoringUpdateEventName)) if err != nil { return err } curResource, err := tx.Fetch(curSchema, transaction.IDFilter(resourceID)) if err != nil { return err } resourceState, err := tx.StateFetch(curSchema, transaction.IDFilter(resourceID)) if err != nil { return err } if resourceState.ConfigVersion != resourceState.StateVersion { return nil } var ok bool monitoringVersion, ok := response.Data["version"].(float64) if !ok { return fmt.Errorf("No version in monitoring information") } if resourceState.ConfigVersion != int64(monitoringVersion) { return nil } resourceState.Monitoring, ok = response.Data["monitoring"].(string) if !ok { return fmt.Errorf("No monitoring in monitoring information") } environmentManager := extension.GetManager() environment, haveEnvironment := environmentManager.GetEnvironment(curSchema.ID) context := map[string]interface{}{} context["resource"] = curResource.Data() context["schema"] = curSchema context["monitoring"] = resourceState.Monitoring context["transaction"] = tx if haveEnvironment { if err := extension.HandleEvent(context, environment, "pre_monitoring_update_in_transaction"); err != nil { return err } } err = tx.StateUpdate(curResource, &resourceState) if err != nil { return err } if haveEnvironment { if err := extension.HandleEvent(context, environment, "post_monitoring_update_in_transaction"); err != nil { return err } } return tx.Commit() }
//MapRouteBySchema setup api route by schema func MapRouteBySchema(server *Server, dataStore db.DB, s *schema.Schema) { if s.IsAbstract() { return } route := server.martini singleURL := s.GetSingleURL() pluralURL := s.GetPluralURL() singleURLWithParents := s.GetSingleURLWithParents() pluralURLWithParents := s.GetPluralURLWithParents() //load extension environments environmentManager := extension.GetManager() if _, ok := environmentManager.GetEnvironment(s.ID); !ok { env, err := server.NewEnvironmentForPath(s.ID, pluralURL) if err != nil { log.Fatal(fmt.Sprintf("[%s] %v", pluralURL, err)) } environmentManager.RegisterEnvironment(s.ID, env) } log.Debug("[Plural Path] %s", pluralURL) log.Debug("[Singular Path] %s", singleURL) log.Debug("[Plural Path With Parents] %s", pluralURLWithParents) log.Debug("[Singular Path With Parents] %s", singleURLWithParents) //setup list route getPluralFunc := func(w http.ResponseWriter, r *http.Request, p martini.Params, identityService middleware.IdentityService, context middleware.Context) { addJSONContentTypeHeader(w) fillInContext(context, dataStore, r, w, s, p, server.sync, identityService, server.queue) if err := resources.GetMultipleResources(context, dataStore, s, r.URL.Query()); err != nil { handleError(w, err) return } w.Header().Add("X-Total-Count", fmt.Sprint(context["total"])) routes.ServeJson(w, context["response"]) } route.Get(pluralURL, middleware.Authorization(schema.ActionRead), getPluralFunc) route.Get(pluralURLWithParents, middleware.Authorization(schema.ActionRead), func(w http.ResponseWriter, r *http.Request, p martini.Params, identityService middleware.IdentityService, context middleware.Context) { addParamToQuery(r, schema.FormatParentID(s.Parent), p[s.Parent]) getPluralFunc(w, r, p, identityService, context) }) //setup show route getSingleFunc := func(w http.ResponseWriter, r *http.Request, p martini.Params, identityService middleware.IdentityService, context middleware.Context) { addJSONContentTypeHeader(w) fillInContext(context, dataStore, r, w, s, p, server.sync, identityService, server.queue) id := p["id"] if err := resources.GetSingleResource(context, dataStore, s, id); err != nil { handleError(w, err) return } routes.ServeJson(w, context["response"]) } route.Get(singleURL, middleware.Authorization(schema.ActionRead), getSingleFunc) route.Get(singleURLWithParents, middleware.Authorization(schema.ActionRead), func(w http.ResponseWriter, r *http.Request, p martini.Params, identityService middleware.IdentityService, context middleware.Context) { addParamToQuery(r, schema.FormatParentID(s.Parent), p[s.Parent]) getSingleFunc(w, r, p, identityService, context) }) //setup delete route deleteSingleFunc := func(w http.ResponseWriter, r *http.Request, p martini.Params, identityService middleware.IdentityService, context middleware.Context) { addJSONContentTypeHeader(w) fillInContext(context, dataStore, r, w, s, p, server.sync, identityService, server.queue) id := p["id"] if err := resources.DeleteResource(context, dataStore, s, id); err != nil { handleError(w, err) return } w.WriteHeader(http.StatusNoContent) } route.Delete(singleURL, middleware.Authorization(schema.ActionDelete), deleteSingleFunc) route.Delete(singleURLWithParents, middleware.Authorization(schema.ActionDelete), func(w http.ResponseWriter, r *http.Request, p martini.Params, identityService middleware.IdentityService, context middleware.Context) { addParamToQuery(r, schema.FormatParentID(s.Parent), p[s.Parent]) deleteSingleFunc(w, r, p, identityService, context) }) //setup create route postPluralFunc := func(w http.ResponseWriter, r *http.Request, p martini.Params, identityService middleware.IdentityService, context middleware.Context) { addJSONContentTypeHeader(w) fillInContext(context, dataStore, r, w, s, p, server.sync, identityService, server.queue) dataMap, err := middleware.ReadJSON(r) if err != nil { handleError(w, resources.NewResourceError(err, fmt.Sprintf("Failed to parse data: %s", err), resources.WrongData)) return } dataMap = removeResourceWrapper(s, dataMap) if s.Parent != "" { if _, ok := dataMap[s.ParentID()]; !ok { queryParams := r.URL.Query() parentIDParam := queryParams.Get(s.ParentID()) if parentIDParam != "" { dataMap[s.ParentID()] = parentIDParam } } } if err := resources.CreateResource(context, dataStore, identityService, s, dataMap); err != nil { handleError(w, err) return } w.WriteHeader(http.StatusCreated) routes.ServeJson(w, context["response"]) } route.Post(pluralURL, middleware.Authorization(schema.ActionCreate), postPluralFunc) route.Post(pluralURLWithParents, middleware.Authorization(schema.ActionCreate), func(w http.ResponseWriter, r *http.Request, p martini.Params, identityService middleware.IdentityService, context middleware.Context) { addParamToQuery(r, schema.FormatParentID(s.Parent), p[s.Parent]) postPluralFunc(w, r, p, identityService, context) }) //setup create or update route putSingleFunc := func(w http.ResponseWriter, r *http.Request, p martini.Params, identityService middleware.IdentityService, context middleware.Context) { addJSONContentTypeHeader(w) fillInContext(context, dataStore, r, w, s, p, server.sync, identityService, server.queue) id := p["id"] dataMap, err := middleware.ReadJSON(r) if err != nil { handleError(w, resources.NewResourceError(err, fmt.Sprintf("Failed to parse data: %s", err), resources.WrongData)) return } dataMap = removeResourceWrapper(s, dataMap) if isCreated, err := resources.CreateOrUpdateResource( context, dataStore, identityService, s, id, dataMap); err != nil { handleError(w, err) return } else if isCreated { w.WriteHeader(http.StatusCreated) } routes.ServeJson(w, context["response"]) } route.Put(singleURL, middleware.Authorization(schema.ActionUpdate), putSingleFunc) route.Put(singleURLWithParents, middleware.Authorization(schema.ActionUpdate), func(w http.ResponseWriter, r *http.Request, p martini.Params, identityService middleware.IdentityService, context middleware.Context) { addParamToQuery(r, schema.FormatParentID(s.Parent), p[s.Parent]) putSingleFunc(w, r, p, identityService, context) }) //setup update route patchSingleFunc := func(w http.ResponseWriter, r *http.Request, p martini.Params, identityService middleware.IdentityService, context middleware.Context) { addJSONContentTypeHeader(w) fillInContext(context, dataStore, r, w, s, p, server.sync, identityService, server.queue) id := p["id"] dataMap, err := middleware.ReadJSON(r) if err != nil { handleError(w, resources.NewResourceError(err, fmt.Sprintf("Failed to parse data: %s", err), resources.WrongData)) return } dataMap = removeResourceWrapper(s, dataMap) if err := resources.UpdateResource( context, dataStore, identityService, s, id, dataMap); err != nil { handleError(w, err) return } routes.ServeJson(w, context["response"]) } route.Patch(singleURL, middleware.Authorization(schema.ActionUpdate), patchSingleFunc) route.Patch(singleURLWithParents, middleware.Authorization(schema.ActionUpdate), func(w http.ResponseWriter, r *http.Request, p martini.Params, identityService middleware.IdentityService, context middleware.Context) { addParamToQuery(r, schema.FormatParentID(s.Parent), p[s.Parent]) patchSingleFunc(w, r, p, identityService, context) }) //Custom action support for _, actionExt := range s.Actions { action := actionExt ActionFunc := func(w http.ResponseWriter, r *http.Request, p martini.Params, identityService middleware.IdentityService, auth schema.Authorization, context middleware.Context) { addJSONContentTypeHeader(w) fillInContext(context, dataStore, r, w, s, p, server.sync, identityService, server.queue) id := p["id"] input := make(map[string]interface{}) if action.InputSchema != nil { var err error input, err = middleware.ReadJSON(r) if err != nil { handleError(w, resources.NewResourceError(err, fmt.Sprintf("Failed to parse data: %s", err), resources.WrongData)) return } } // TODO use authorization middleware manager := schema.GetManager() path := r.URL.Path policy, role := manager.PolicyValidate(action.ID, path, auth) if policy == nil { middleware.HTTPJSONError(w, fmt.Sprintf("No matching policy: %s %s %s", action, path, s.Actions), http.StatusUnauthorized) return } context["policy"] = policy context["tenant_id"] = auth.TenantID() context["auth_token"] = auth.AuthToken() context["role"] = role context["catalog"] = auth.Catalog() context["auth"] = auth if err := resources.ActionResource( context, dataStore, identityService, s, action, id, input); err != nil { handleError(w, err) return } routes.ServeJson(w, context["response"]) } route.AddRoute(action.Method, s.GetActionURL(action.Path), ActionFunc) } }
//MonitoringUpdate updates the state in the db based on the sync event func MonitoringUpdate(response *gohan_sync.Event, server *Server) error { lockKey := lockPath + response.Key err := server.sync.Lock(lockKey, false) if err != nil { return err } defer func() { server.sync.Unlock(lockKey) }() dataStore := server.db schemaPath := "/" + strings.TrimPrefix(response.Key, monitoringPrefix) var curSchema *schema.Schema manager := schema.GetManager() for _, s := range manager.Schemas() { if strings.HasPrefix(schemaPath, s.URL) { curSchema = s break } } if curSchema == nil || !curSchema.StateVersioning() { log.Debug("Monitoring update on unexpected path '%s'", schemaPath) return nil } resourceID := strings.TrimPrefix(schemaPath, curSchema.URL+"/") tx, err := dataStore.Begin() if err != nil { return err } defer tx.Close() curResource, err := tx.Fetch(curSchema, resourceID, nil) if err != nil { return err } resourceState, err := tx.StateFetch(curSchema, resourceID, nil) if err != nil { return err } if resourceState.ConfigVersion != resourceState.StateVersion { return nil } var ok bool resourceState.Monitoring, ok = response.Data["monitoring"].(string) if !ok { return fmt.Errorf("No monitoring in state information") } environmentManager := extension.GetManager() environment, haveEnvironment := environmentManager.GetEnvironment(curSchema.ID) context := map[string]interface{}{} context["resource"] = curResource.Data() context["monitoring"] = resourceState.Monitoring context["transaction"] = tx if haveEnvironment { if err := extension.HandleEvent(context, environment, "pre_monitoring_update_in_transaction"); err != nil { return err } } err = tx.StateUpdate(curResource, &resourceState) if err != nil { return err } if haveEnvironment { if err := extension.HandleEvent(context, environment, "post_monitoring_update_in_transaction"); err != nil { return err } } return tx.Commit() }
"github.com/cloudwan/gohan/extension/otto" "github.com/cloudwan/gohan/schema" "github.com/cloudwan/gohan/server/middleware" "github.com/cloudwan/gohan/server/resources" "github.com/cloudwan/gohan/util" ) var _ = Describe("Otto extension manager", func() { var ( manager *schema.Manager environmentManager *extension.Manager ) BeforeEach(func() { manager = schema.GetManager() environmentManager = extension.GetManager() }) AfterEach(func() { tx, err := testDB.Begin() Expect(err).ToNot(HaveOccurred(), "Failed to create transaction.") defer tx.Close() for _, schema := range schema.GetManager().Schemas() { if whitelist[schema.ID] { continue } err = clearTable(tx, schema) Expect(err).ToNot(HaveOccurred(), "Failed to clear table.") } err = tx.Commit() Expect(err).ToNot(HaveOccurred(), "Failed to commite transaction.")