Пример #1
0
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)
}
Пример #2
0
//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
}
Пример #3
0
//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
}
Пример #4
0
//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
}
Пример #5
0
// 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
}
Пример #7
0
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
}
Пример #8
0
// 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
}
Пример #9
0
//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
}
Пример #10
0
// 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")
}
Пример #11
0
// 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
}
Пример #12
0
// 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
}
Пример #13
0
//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
}
Пример #14
0
//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
}
Пример #15
0
// 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
}
Пример #16
0
//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()
}
Пример #17
0
// 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
}
Пример #18
0
//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()
}
Пример #19
0
		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() {
Пример #20
0
//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()
}
Пример #21
0
//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)
	}
}
Пример #22
0
//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()
}
Пример #23
0
	"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.")