Skip to content

Latest commit

 

History

History
executable file
·
2184 lines (1546 loc) · 84.6 KB

DOCUMENTATION.md

File metadata and controls

executable file
·
2184 lines (1546 loc) · 84.6 KB

Go Report Card GitHub Actions Go Reference Code Coverage

authorizer

import "github.com/vmware-labs/multi-tenant-persistence-for-saas/pkg/authorizer"

Index

Constants

const (
    GLOBAL_DEFAULT_ORG_ID = "_GlobalDefaultOrg"

    METADATA_KEY_ORGID            = "orgid"
    METADATA_KEY_ROLE             = "role"
    METADATA_ROLE_SERVICE_ADMIN   = "service_admin"
    METADATA_ROLE_SERVICE_AUDITOR = "service_auditor"
    METADATA_ROLE_ADMIN           = "admin"   // can be tenant_admin, *_admin
    METADATA_ROLE_AUDITOR         = "auditor" // can be tenant_auditor, *_auditor
)

const (
    INSTANCE_ID = ContextKey("multiinstance.id")
)

const (
    TransactionCtx = TransactionContextKey("DB_TRANSACTION")
)

Authorizer Interface defines the methods required for datastore to restrict access based on roles configured in context.

type Authorizer interface {
    Tenancer
    Configure(tableName string, roleMapping map[string]dbrole.DbRole)
    GetAuthContext(orgId string, roles ...string) context.Context
    GetDefaultOrgAdminContext() context.Context
    GetMatchingDbRole(ctx context.Context, tableNames ...string) (dbrole.DbRole, error)
}

type ContextKey string

type ContextLessAuthorizer struct {
    // contains filtered or unexported fields
}

func (*ContextLessAuthorizer) Configure

func (s *ContextLessAuthorizer) Configure(tableName string, roleMapping map[string]dbrole.DbRole)

func (*ContextLessAuthorizer) GetAuthContext

func (s *ContextLessAuthorizer) GetAuthContext(orgId string, roles ...string) context.Context

func (*ContextLessAuthorizer) GetDefaultOrgAdminContext

func (s *ContextLessAuthorizer) GetDefaultOrgAdminContext() context.Context

func (*ContextLessAuthorizer) GetMatchingDbRole

func (s *ContextLessAuthorizer) GetMatchingDbRole(_ context.Context, tableNames ...string) (dbrole.DbRole, error)

func (*ContextLessAuthorizer) GetOrgFromContext

func (s *ContextLessAuthorizer) GetOrgFromContext(_ context.Context) (string, error)

type Instancer interface {
    GetInstanceId(ctx context.Context) (string, error)
    WithInstanceId(ctx context.Context, instanceId string) context.Context
}

type MetadataBasedAuthorizer struct {
    // contains filtered or unexported fields
}

func (*MetadataBasedAuthorizer) Configure

func (s *MetadataBasedAuthorizer) Configure(tableName string, roleMapping map[string]dbrole.DbRole)

func (*MetadataBasedAuthorizer) GetAuthContext

func (s *MetadataBasedAuthorizer) GetAuthContext(orgId string, roles ...string) context.Context

func (*MetadataBasedAuthorizer) GetDefaultOrgAdminContext

func (s *MetadataBasedAuthorizer) GetDefaultOrgAdminContext() context.Context

func (*MetadataBasedAuthorizer) GetMatchingDbRole

func (s *MetadataBasedAuthorizer) GetMatchingDbRole(ctx context.Context, tableNames ...string) (dbrole.DbRole, error)

func (*MetadataBasedAuthorizer) GetOrgFromContext

func (s *MetadataBasedAuthorizer) GetOrgFromContext(ctx context.Context) (string, error)

type SimpleInstancer struct{}

func (*SimpleInstancer) GetInstanceId

func (s *SimpleInstancer) GetInstanceId(ctx context.Context) (string, error)

func (*SimpleInstancer) WithInstanceId

func (s *SimpleInstancer) WithInstanceId(ctx context.Context, instanceId string) context.Context

type SimpleTransactionFetcher struct{}

func (SimpleTransactionFetcher) GetTransactionCtx

func (s SimpleTransactionFetcher) GetTransactionCtx(ctx context.Context) *gorm.DB

func (SimpleTransactionFetcher) IsTransactionCtx

func (s SimpleTransactionFetcher) IsTransactionCtx(ctx context.Context) bool

func (SimpleTransactionFetcher) WithTransactionCtx

func (s SimpleTransactionFetcher) WithTransactionCtx(ctx context.Context, tx *gorm.DB) context.Context

type Tenancer interface {
    GetOrgFromContext(ctx context.Context) (string, error)
}

type TransactionContextKey string

type TransactionFetcher interface {
    IsTransactionCtx(ctx context.Context) bool
    GetTransactionCtx(ctx context.Context) *gorm.DB
    WithTransactionCtx(ctx context.Context, tx *gorm.DB) context.Context
}

datastore

import "github.com/vmware-labs/multi-tenant-persistence-for-saas/pkg/datastore"

Import the package and use DataStore interface to interact with the data access layer. If you want DAL to use a Postgres database, ensure you have the following environment variables set to relevant values: [DB_ADMIN_USERNAME], [DB_PORT], [DB_NAME], [DB_ADMIN_PASSWORD], [DB_HOST], [SSL_MODE]. You can also set [LOG_LEVEL] environment variable to debug/trace, if you want logging at a specific level (default is [Info])

Define structs that will be persisted using datastore similar to any gorm Models, for reference https://gorm.io/docs/models.html

  • At least one field must be a primary key with `gorm:"primaryKey"` tag
  • For multi-tenancy support, add `gorm:"column:org_id"` as tag to a filed
  • For revision support to block concurrent updates, add `gorm:"column:revision"` as tag
  • For multi-instance support, add `gorm:"column:instance_id"` as tag

DataStore interface exposes basic methods like Find/FindAll/Upsert/Delete. For richer queries and performing a set of operations within a transaction, please, use GetTransaction() method. For more info, refer to Gorm's transactions page: https://gorm.io/docs/transactions.html

Index

Constants

const (
    DbConfigOrgId      = "multitenant.orgId"      // Name of Postgres run-time config. parameter that will store current user's org. ID
    DbConfigInstanceId = "multitenant.instanceId" // Name of Postgres run-time config. parameter that will store current session's instance ID

    MaxIdleConns = 1
)

const (
    // Env. variable names.
    DB_NAME_ENV_VAR           = "DB_NAME"
    DB_PORT_ENV_VAR           = "DB_PORT"
    DB_HOST_ENV_VAR           = "DB_HOST"
    SSL_MODE_ENV_VAR          = "SSL_MODE"
    DB_ADMIN_USERNAME_ENV_VAR = "DB_ADMIN_USERNAME"
    DB_ADMIN_PASSWORD_ENV_VAR = "DB_ADMIN_PASSWORD"

    // SQL Error Codes.
    ERROR_DUPLICATE_KEY      = "SQLSTATE 23505"
    ERROR_DUPLICATE_DATABASE = "SQLSTATE 42P04"
)

const (
    DEFAULT_OFFSET = 0
    DEFAULT_LIMIT  = 1000
    DEFAULT_SORTBY = ""
)

const (
    // Struct Field Names.
    FIELD_ORGID      = "OrgId"
    FIELD_INSTANCEID = "InstanceId"

    // SQL Columns.
    COLUMN_ORGID      = "org_id"
    COLUMN_INSTANCEID = "instance_id"
    COLUMN_REVISION   = "revision"

    // Messages.
    REVISION_OUTDATED_MSG = "Invalid update - outdated "
)

func DBCreate(cfg DBConfig) error

Create a Postgres DB using the provided config if it doesn't exist.

func DBExists(cfg DBConfig) bool

Checks if a Postgres DB exists and returns true.

func GetFieldValue(record Record, fieldName, columnName string) (string, bool)

Returns the requested fields value from record, which is a pointer to a struct implementing Record interface. Uses a tag rather than field name to find the desired field. Returns an empty string and false if such a field is not present.

func GetInstanceId(record Record) (string, bool)

Returns the requested InstanceId field's value from record, which is a pointer to a struct implementing Record interface. Uses a tag rather than field name to find the desired field. Returns an empty string and false if such a field is not present.

func GetOrgId(record Record) (string, bool)

Returns the requested OrgId field's value from record, which is a pointer to a struct implementing Record interface. Uses a tag rather than field name to find the desired field. Returns an empty string and false if such a field is not present.

func GetTableName(x interface{}) (tableName string)

Extracts struct's name, which will serve as DB table name, using reflection.

func IsColumnPresent(x Record, tableName, columnName string) bool

func IsMultiInstanced(x Record, tableName string, instancerConfigured bool) bool

Checks if multiple deployment instances are supported in the given table.

func IsMultiTenanted(x Record, tableName string) bool

Checks if multiple tenants are supported in the given table.

func IsPointerToStruct(x interface{}) (isPtrType bool)

func IsRevisioned(x Record, tableName string) bool

Checks if revisioning is supported in the given table.

func IsRowLevelSecurityRequired(record Record, tableName string, instancerConfigured bool) bool

Row Level Security to used to partition tables for multi-tenancy and multi-instance support.

func SetFieldValue(record Record, fieldName, columnName, value string) bool

func SetInstanceId(record Record, value string) bool

func TypeName(x interface{}) string

TypeName returns name of the data type of the given variable.

type DBConfig struct {
    // contains filtered or unexported fields
}

func ConfigFromEnv(dbName string) DBConfig

Returns DBConfig constructed from the env variables. If dbName is set, it is used instead of DB_NAME env variable. All env variables are required and if not set, this method would panic.

type DataStore interface {
    Find(ctx context.Context, record Record) error
    FindSoftDeleted(ctx context.Context, record Record) error
    FindAll(ctx context.Context, records interface{}, pagination *Pagination) error
    FindAllIncludingSoftDeleted(ctx context.Context, records interface{}, pagination *Pagination) error
    FindWithFilter(ctx context.Context, filter Record, records interface{}, pagination *Pagination) error
    FindWithFilterIncludingSoftDeleted(ctx context.Context, filter Record, records interface{}, pagination *Pagination) error
    Insert(ctx context.Context, record Record) (int64, error)
    SoftDelete(ctx context.Context, record Record) (int64, error)
    Delete(ctx context.Context, record Record) (int64, error)
    Update(ctx context.Context, record Record) (int64, error)
    Upsert(ctx context.Context, record Record) (int64, error)
    GetTransaction(ctx context.Context, record ...Record) (tx *gorm.DB, err error)

    // Create a DB table for the given struct. Enables RLS in it if it is multi-tenant.
    // Generates Postgres roles and policies based on the provided role mapping and applies them
    // to the created DB table.
    // roleMapping - maps service roles to DB roles to be used for the generated DB table
    // There are 4 possible DB roles to choose from:
    // - READER, which gives read access to all the records in the table
    // - WRITER, which gives read & write access to all the records in the table
    // - TENANT_READER, which gives read access to current tenant's records
    // - TENANT_WRITER, which gives read & write access to current tenant's records.
    Register(ctx context.Context, roleMapping map[string]dbrole.DbRole, records ...Record) error
    Reset()

    GetAuthorizer() authorizer.Authorizer
    GetInstancer() authorizer.Instancer
    Helper() Helper
    TestHelper() TestHelper
}
Example (Multi Instance)

Example for the multi-instance feature, illustrates one can create records for different instances and that each instance context can access the data that belongs to specific instance only.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/vmware-labs/multi-tenant-persistence-for-saas/pkg/authorizer"
	"github.com/vmware-labs/multi-tenant-persistence-for-saas/pkg/datastore"
	"github.com/vmware-labs/multi-tenant-persistence-for-saas/pkg/dbrole"
	"github.com/vmware-labs/multi-tenant-persistence-for-saas/pkg/logutils"
)

type Person struct {
	Id         string `gorm:"primaryKey"`
	Name       string
	Age        int
	InstanceId string `gorm:"primaryKey"`
}

func (p Person) String() string {
	return fmt.Sprintf("[%s/%s] %s: %d", p.InstanceId, p.Id, p.Name, p.Age)
}

// Example for the multi-instance feature, illustrates one can create records for different instances
// and that each instance context can access the data that belongs to specific instance only.
func main() {
	uId := "P1337"
	p1 := &Person{uId, "Bob", 31, "Dev"}
	p2 := &Person{uId, "John", 36, "Prod"}
	p3 := &Person{"P3", "Pat", 39, "Dev"}

	SERVICE_ADMIN := "service_admin"
	SERVICE_AUDITOR := "service_auditor"
	mdAuthorizer := &authorizer.MetadataBasedAuthorizer{}
	instancer := &authorizer.SimpleInstancer{}

	ServiceAdminCtx := mdAuthorizer.GetAuthContext("", SERVICE_ADMIN)
	DevInstanceCtx := instancer.WithInstanceId(ServiceAdminCtx, "Dev")
	ProdInstanceCtx := instancer.WithInstanceId(ServiceAdminCtx, "Prod")

	// Initializes the Datastore using the metadata authorizer and connection details obtained from the ENV variables.
	ds, err := datastore.FromEnvWithDB(logutils.GetCompLogger(), mdAuthorizer, instancer, "ExampleDataStore_multiInstance")
	defer ds.Reset()
	if err != nil {
		log.Fatalf("datastore initialization from env errored: %s", err)
	}

	// Registers the necessary structs with their corresponding role mappings.
	roleMapping := map[string]dbrole.DbRole{
		SERVICE_AUDITOR: dbrole.INSTANCE_READER,
		SERVICE_ADMIN:   dbrole.INSTANCE_WRITER,
	}
	if err = ds.Register(context.TODO(), roleMapping, &Person{}); err != nil {
		log.Fatalf("Failed to create DB tables: %+v", err)
	}

	// Inserts a record with a given Id (uId) using the context of the Dev instance.
	rowsAffected, err := ds.Insert(DevInstanceCtx, p1)
	fmt.Println(rowsAffected, err)
	// Inserts another record with the same uId using the context of the Prod instance.
	rowsAffected, err = ds.Insert(ProdInstanceCtx, p2)
	fmt.Println(rowsAffected, err)
	// Inserts a third record with a different uId using the context of the Dev instance.
	rowsAffected, err = ds.Insert(DevInstanceCtx, p3)
	fmt.Println(rowsAffected, err)

	// Finds a record using the context of the Dev instance and the specified uId.
	q1 := &Person{Id: uId}
	err = ds.Find(DevInstanceCtx, q1)
	fmt.Println(q1, err)
	// Finds a record using the context of the Prod instance and the same uId.
	q2 := &Person{Id: uId}
	err = ds.Find(ProdInstanceCtx, q2)
	fmt.Println(q2, err)
	// Finds a record using the correct context of the Dev instance.
	q3 := &Person{Id: "P3"}
	err = ds.Find(DevInstanceCtx, q3)
	fmt.Println(q3, err)
	// Attempts to find a record using the incorrect context of the Prod instance and should error out.
	q4 := &Person{Id: "P3"}
	err = ds.Find(ProdInstanceCtx, q4)
	fmt.Printf("err != nil - %t\n", err != nil)

	// Deletes a record using the context of the Dev instance and the specified uId.
	rowsAffected, err = ds.Delete(DevInstanceCtx, q1)
	fmt.Println(rowsAffected, err)
	// Deletes a record using the context of the Prod instance and the same uId.
	rowsAffected, err = ds.Delete(ProdInstanceCtx, q2)
	fmt.Println(rowsAffected, err)
	// Attempts to delete a record using an invalid context of the Prod instance, which should not affect the database.
	rowsAffected, err = ds.Delete(ProdInstanceCtx, q4)
	fmt.Println(rowsAffected, err)
	// Deletes a record using a valid context of the Dev instance.
	rowsAffected, err = ds.Delete(DevInstanceCtx, q3)
	fmt.Println(rowsAffected, err)
}

Output

1 <nil>
1 <nil>
1 <nil>
[Dev/P1337] Bob: 31 <nil>
[Prod/P1337] John: 36 <nil>
[Dev/P3] Pat: 39 <nil>
err != nil - true
1 <nil>
1 <nil>
0 <nil>
1 <nil>

Example (Multi Tenancy)

Example for the multi-tenancy feature, illustrates one can create records for different tenants and that each tenant context can access the data that belongs to specific tenant only.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/vmware-labs/multi-tenant-persistence-for-saas/pkg/authorizer"
	"github.com/vmware-labs/multi-tenant-persistence-for-saas/pkg/datastore"
	"github.com/vmware-labs/multi-tenant-persistence-for-saas/pkg/dbrole"
	"github.com/vmware-labs/multi-tenant-persistence-for-saas/pkg/logutils"
)

type User struct {
	Id    string `gorm:"primaryKey"`
	Name  string
	Age   int
	OrgId string `gorm:"primaryKey"`
}

func (p User) String() string {
	return fmt.Sprintf("[%s/%s] %s: %d", p.OrgId, p.Id, p.Name, p.Age)
}

// Example for the multi-tenancy feature, illustrates one can create records for different tenants
// and that each tenant context can access the data that belongs to specific tenant only.
func main() {
	uId := "P1337"
	p1 := &User{uId, "Bob", 31, "Coke"}
	p2 := &User{uId, "John", 36, "Pepsi"}
	p3 := &User{"P3", "Pat", 39, "Coke"}

	TENANT_ADMIN := "tenant_admin"
	TENANT_AUDITOR := "tenant_auditor"
	mdAuthorizer := &authorizer.MetadataBasedAuthorizer{}
	CokeOrgCtx := mdAuthorizer.GetAuthContext("Coke", TENANT_ADMIN)
	PepsiOrgCtx := mdAuthorizer.GetAuthContext("Pepsi", TENANT_ADMIN)

	// Initializes the Datastore using the metadata authorizer and connection details obtained from the ENV variables.
	ds, err := datastore.FromEnvWithDB(logutils.GetCompLogger(), mdAuthorizer, nil, "ExampleDataStore_multiTenancy")
	defer ds.Reset()
	if err != nil {
		log.Fatalf("datastore initialization from env errored: %s", err)
	}

	// Registers the necessary structs with their corresponding tenant role mappings.
	roleMapping := map[string]dbrole.DbRole{
		TENANT_AUDITOR: dbrole.TENANT_READER,
		TENANT_ADMIN:   dbrole.TENANT_WRITER,
	}
	if err = ds.Register(context.TODO(), roleMapping, &User{}); err != nil {
		log.Fatalf("Failed to create DB tables: %+v", err)
	}

	// Inserts a record with a given Id (uId) using the context of the Coke organization.
	rowsAffected, err := ds.Insert(CokeOrgCtx, p1)
	fmt.Println(rowsAffected, err)
	// Inserts another record with the same uId using the context of the Pepsi organization.
	rowsAffected, err = ds.Insert(PepsiOrgCtx, p2)
	fmt.Println(rowsAffected, err)
	// Inserts a third record with a different uId using the context of the Coke organization.
	rowsAffected, err = ds.Insert(CokeOrgCtx, p3)
	fmt.Println(rowsAffected, err)

	// Finds a record using the context of the Coke organization and the specified uId.
	q1 := &User{Id: uId}
	err = ds.Find(CokeOrgCtx, q1)
	fmt.Println(q1, err)
	// Finds a record using the context of the Pepsi organization and the same uId.
	q2 := &User{Id: uId}
	err = ds.Find(PepsiOrgCtx, q2)
	fmt.Println(q2, err)
	// Finds a record using the correct context of the Coke organization.
	q3 := &User{Id: "P3"}
	err = ds.Find(CokeOrgCtx, q3)
	fmt.Println(q3, err)
	// Attempts to find a record using the incorrect context of the Pepsi organization and should error out.
	q4 := &User{Id: "P3"}
	err = ds.Find(PepsiOrgCtx, q4)
	fmt.Printf("err != nil - %t\n", err != nil)

	// Deletes a record using the context of the Coke organization and the specified uId.
	rowsAffected, err = ds.Delete(CokeOrgCtx, q1)
	fmt.Println(rowsAffected, err)
	// Deletes a record using the context of the Pepsi organization and the same uId.
	rowsAffected, err = ds.Delete(PepsiOrgCtx, q2)
	fmt.Println(rowsAffected, err)
	// Attempts to delete a record using an invalid context of the Pepsi organization, which should not affect the database.
	rowsAffected, err = ds.Delete(PepsiOrgCtx, q4)
	fmt.Println(rowsAffected, err)
	// Deletes a record using a valid context of the Coke organization.
	rowsAffected, err = ds.Delete(CokeOrgCtx, q3)
	fmt.Println(rowsAffected, err)

}

Output

1 <nil>
1 <nil>
1 <nil>
[Coke/P1337] Bob: 31 <nil>
[Pepsi/P1337] John: 36 <nil>
[Coke/P3] Pat: 39 <nil>
err != nil - true
1 <nil>
1 <nil>
0 <nil>
1 <nil>

func FromConfig(l *logrus.Entry, a authorizer.Authorizer, instancer authorizer.Instancer, cfg DBConfig) (d DataStore, err error)

func FromEnv

func FromEnv(l *logrus.Entry, a authorizer.Authorizer, instancer authorizer.Instancer) (d DataStore, err error)

func FromEnvWithDB(l *logrus.Entry, a authorizer.Authorizer, instancer authorizer.Instancer, dbName string) (d DataStore, err error)

type Helper

type Helper interface {
    FindInTable(ctx context.Context, tableName string, record Record, softDelete bool) (err error)
    FindAllInTable(ctx context.Context, tableName string, records interface{}, pagination *Pagination, softDelete bool) error
    FindWithFilterInTable(ctx context.Context, tableName string, record Record, records interface{}, pagination *Pagination, softDelete bool) (err error)
    GetDBTransaction(ctx context.Context, tableName string, record Record) (tx *gorm.DB, err error)

    RegisterHelper(ctx context.Context, roleMapping map[string]dbrole.DbRole, tableName string, record Record) error
}

type Pagination struct {
    Offset int
    Limit  int
    SortBy string
}

func DefaultPagination() *Pagination

func GetPagination(offset int, limit int, sortBy string) *Pagination

func NoPagination() *Pagination

type Record

type Record interface {
}

func GetRecordInstanceFromSlice(x interface{}) Record

type TenancyInfo struct {
    DbRole     dbrole.DbRole
    InstanceId string
    OrgId      string
}

type TestHelper interface {
    DropTables(records ...Record) error                       // Drop DB tables by records
    Truncate(tableNames ...string) error                      // Truncates DB tables
    TruncateCascade(cascade bool, tableNames ...string) error // Truncates DB tables, with an option to truncate them in a cascading fashion
    HasTable(tableName string) (bool, error)                  // Checks if DB table exists
}

dbrole

import "github.com/vmware-labs/multi-tenant-persistence-for-saas/pkg/dbrole"

Corresponding *INSTANCE_* roles access is determined by the Instancer's configuration, allowing it to access records exclusively with a specific instance.

  • `TENANT_INSTANCE_READER` - has read access to its tenant instance's data
  • `INSTANCE_READER` - has read access to specific instance data
  • `TENANT_INSTANCE_WRITER` - has read & write access to its tenant instance's data
  • `INSTANCE_WRITER` - has read & write access to specific instance data

DAL allows to map a user's service role to the DB role that will be used for that user. If a user has multiple service roles which map to several DB roles, the DB role with the most extensive privileges will be used (see `DbRoles()` for reference to ordered list of DbRoles.

Index

type DbRole

DbRole Database roles/users.

type DbRole string

const (
    // NO_ROLE DB Roles.
    NO_ROLE                DbRole = ""
    TENANT_INSTANCE_READER DbRole = "tenant_instance_reader"
    TENANT_READER          DbRole = "tenant_reader"
    INSTANCE_READER        DbRole = "instance_reader"
    READER                 DbRole = "reader"
    TENANT_INSTANCE_WRITER DbRole = "tenant_instance_writer"
    TENANT_WRITER          DbRole = "tenant_writer"
    INSTANCE_WRITER        DbRole = "instance_writer"
    WRITER                 DbRole = "writer"
    MAIN                   DbRole = "main"
)

func Max

func Max(dbRoles []DbRole) DbRole

func Min

func Min(dbRoles []DbRole) DbRole

func (DbRole) GetRoleWithInstancer

func (dbRole DbRole) GetRoleWithInstancer() DbRole

Map roles to instancer based when Instancer is set. Useful for backward compatibility when role Mapping do not reference *INSTANCE* roles, but an Instancer is configured to limit the access to an instance.

func (dbRole DbRole) IsDbRoleInstanceScoped() bool

func (DbRole) IsDbRoleTenantScoped

func (dbRole DbRole) IsDbRoleTenantScoped() bool

type DbRoleSlice []DbRole // Needed for sorting records

func DbRoles

func DbRoles() DbRoleSlice

Returns *Ordered* slice of DbRoles. A reader role is always considered to have fewer permissions than a writer role. and a tenant-specific reader/writer role is always considered to have fewer permissions, than a non-tenant specific reader/writer role, respectively.

func (DbRoleSlice) Len

func (a DbRoleSlice) Len() int

func (DbRoleSlice) Less

func (a DbRoleSlice) Less(i, j int) bool

Returns true if the first role has fewer permissions than the second role, and true if the two roles are the same or the second role has more permissions.

func (DbRoleSlice) Swap

func (a DbRoleSlice) Swap(i, j int)

errors

import "github.com/vmware-labs/multi-tenant-persistence-for-saas/pkg/errors"

Index

Constants

const (
    ENV_VAR           = ErrorContextKey("EnvVar")
    VALUE             = ErrorContextKey("Value")
    DB_ADMIN_USERNAME = ErrorContextKey("dbAdminUsername")
    DB_HOST           = ErrorContextKey("dbHost")
    DB_NAME           = ErrorContextKey("dbName")
    DB_PORT           = ErrorContextKey("dbPort")
    DB_USERNAME       = ErrorContextKey("dbUsername")
    PORT_NUMBER       = ErrorContextKey("PortNumber")
    SQL_STMT          = ErrorContextKey("SqlStmt")
    SSL_MODE          = ErrorContextKey("sslMode")
    TABLE_NAME        = ErrorContextKey("TableName")
    TYPE              = ErrorContextKey("Type")
    DB_ROLE           = ErrorContextKey("DbRole")
)

Variables

var (
    ErrBaseDb              = &DbError{}
    ErrMissingRoleMapping  = ErrBaseDb.With("Missing role mapping for DB table")
    ErrNotPtrToStructSlice = ErrBaseDb.With("Argument is invalid")
    ErrInvalidPortNumber   = ErrBaseDb.With("Port # is invalid")
    ErrMissingEnvVar       = ErrBaseDb.With("An environment variable is missing or empty")
    ErrMissingOrgId        = ErrBaseDb.With("OrgId is missing in context")
    ErrConnectingToDb      = ErrBaseDb.With("Failed to establish a connection with database")
    ErrExecutingSqlStmt    = ErrBaseDb.With("SQL statement could not be executed")
    ErrRegisteringStruct   = ErrBaseDb.With("Registration of a struct with DAL failed")
    ErrStartingTx          = ErrBaseDb.With("Failed to start a transaction")
    ErrCommittingTx        = ErrBaseDb.With("Failed to commit a transaction")
    ErrRevisionConflict    = ErrBaseDb.With("Blocking update due to outdated revision")
    ErrMarshalling         = ErrBaseDb.With("Cannot marshal proto message to binary")
    ErrUnmarshalling       = ErrBaseDb.With("Cannot unmarshal binary to proto message")
    ErrOperationNotAllowed = ErrBaseDb.With("Not authorized to perform the operation on other's data")
    ErrFetchingMetadata    = ErrBaseDb.With("Error fetching metadata from GRPC context")
    ErrTableDoesNotExist   = ErrBaseDb.With("Table does not exist")
    ErrRecordNotFound      = ErrBaseDb.With("Unable to locate record")
    ErrNotPtrToStruct      = ErrBaseDb.With("PointerToStruct expected, invalid type provided")

    ErrAuthContext       = ErrBaseDb.With("Error extracting authContext from context")
    ErrNoAuthContext     = ErrBaseDb.With("Permission denied because authContext is missing")
    ErrNoUserContext     = ErrBaseDb.With("Permission denied because userInformation is missing")
    ErrUserNotAuthorized = ErrBaseDb.With("User is not authorized to access this API")
    ErrMissingInstanceId = ErrBaseDb.With("Instance ID is not configured in the context")

    ErrMarkingEnforcementFailed = ErrBaseDb.With("Failed to mark resource's enforcement status")
    ErrGettingRealizationStatus = ErrBaseDb.With("Failed to get resource's realization status")
    ErrResourceStillExists      = ErrBaseDb.With("Resource still exists")
)

type DbError

type DbError struct {
    // contains filtered or unexported fields
}

func (*DbError) Error

func (e *DbError) Error() string

func (*DbError) Is

func (e *DbError) Is(target error) bool

func (*DbError) Unwrap

func (e *DbError) Unwrap() error

func (*DbError) With

func (e *DbError) With(msg string) *DbError

func (*DbError) WithContext

func (e *DbError) WithContext(ctx context.Context) *DbError

func (*DbError) WithMap

func (e *DbError) WithMap(kvMap map[ErrorContextKey]string) *DbError

func (*DbError) WithValue

func (e *DbError) WithValue(key ErrorContextKey, value string) *DbError

func (*DbError) Wrap

func (e *DbError) Wrap(err error) *DbError

type ErrorContextKey string

func (ErrorContextKey) String

func (c ErrorContextKey) String() string

protostore

import "github.com/vmware-labs/multi-tenant-persistence-for-saas/pkg/protostore"

This package exposes interface [pkg.protostore.ProtoStore] to the consumer, which is a wrapper around [pkg.datastore.DataStore] interface and is used specifically to persist Protobuf messages. Just as with [pkg.datastore.DataStore], Protobuf messages can be persisted with revisioning and multi-tenancy support along with `CreatedAt` and `UpdatedAt` timestamps. Tombstone Delete or soft deletes are supported with `DeletedAt` struct field. Use Delete to remove any records that are soft deleted but still in database

Index

func FromBytes(bytes []byte, message proto.Message) error

func ToBytes

func ToBytes(message proto.Message) ([]byte, error)

type Metadata struct {
    Id         string
    InstanceId string
    ParentId   string
    Revision   int64
    CreatedAt  time.Time
    UpdatedAt  time.Time
    DeletedAt  gorm.DeletedAt
}

func MetadataFrom(protoStoreMsg ProtoStoreMsg) Metadata

type ProtoStore interface {
    Register(ctx context.Context, roleMapping map[string]dbrole.DbRole, msgs ...proto.Message) error
    Insert(ctx context.Context, id string, msg proto.Message) (rowsAffected int64, md Metadata, err error)
    Update(ctx context.Context, id string, msg proto.Message) (rowsAffected int64, md Metadata, err error)
    Upsert(ctx context.Context, id string, msg proto.Message) (rowsAffected int64, md Metadata, err error)
    FindById(ctx context.Context, id string, msg proto.Message, metadata *Metadata) error
    FindByIdIncludingSoftDeleted(ctx context.Context, id string, msg proto.Message, metadata *Metadata) error
    FindAll(ctx context.Context, msgs interface{}, pagination *datastore.Pagination) (metadataMap map[string]Metadata, err error)
    FindAllIncludingSoftDeleted(ctx context.Context, msgs interface{}, pagination *datastore.Pagination) (metadataMap map[string]Metadata, err error)
    FindAllAsMap(ctx context.Context, msgsMap interface{}, pagination *datastore.Pagination) (metadataMap map[string]Metadata, err error)
    SoftDeleteById(ctx context.Context, id string, msg proto.Message) (rowsAffected int64, md Metadata, err error)
    DeleteById(ctx context.Context, id string, msg proto.Message) (rowsAffected int64, err error)

    InsertWithMetadata(ctx context.Context, id string, msg proto.Message, metadata Metadata) (rowsAffected int64, md Metadata, err error)
    UpdateWithMetadata(ctx context.Context, id string, msg proto.Message, metadata Metadata) (rowsAffected int64, md Metadata, err error)
    UpsertWithMetadata(ctx context.Context, id string, msg proto.Message, metadata Metadata) (rowsAffected int64, md Metadata, err error)

    GetMetadata(ctx context.Context, id string, msg proto.Message) (md Metadata, err error)
    GetSoftDeletedMetadata(ctx context.Context, id string, msg proto.Message) (md Metadata, err error)
    GetRevision(ctx context.Context, id string, msg proto.Message) (rowsAffected int64, err error)

    MsgToFilter(ctx context.Context, id string, msg proto.Message) (pMsg *ProtoStoreMsg, err error)
    MsgToPersist(ctx context.Context, id string, msg proto.Message, md Metadata) (pMsg *ProtoStoreMsg, err error)

    GetDataStore() datastore.DataStore
    GetAuthorizer() authorizer.Authorizer
    DropTables(msgs ...proto.Message) error
}
Example

package main

import (
	"context"
	"fmt"

	"github.com/vmware-labs/multi-tenant-persistence-for-saas/pkg/authorizer"
	"github.com/vmware-labs/multi-tenant-persistence-for-saas/pkg/datastore"
	"github.com/vmware-labs/multi-tenant-persistence-for-saas/pkg/dbrole"
	"github.com/vmware-labs/multi-tenant-persistence-for-saas/pkg/logutils"
	"github.com/vmware-labs/multi-tenant-persistence-for-saas/pkg/protostore"
	"github.com/vmware-labs/multi-tenant-persistence-for-saas/test/pb"
)

func main() {
	// Initialize protostore with proper logger, authorizer and datastore
	myLogger := logutils.GetCompLogger()
	mdAuthorizer := &authorizer.MetadataBasedAuthorizer{}
	myDatastore, _ := datastore.FromEnvWithDB(myLogger, mdAuthorizer, nil, "ExampleProtoStore")
	defer myDatastore.Reset()
	ctx := mdAuthorizer.GetAuthContext("Coke", "service_admin")
	myProtostore := protostore.GetProtoStore(myLogger, myDatastore)

	// Register protobufs with proper roleMappings
	roleMappingForMemory := map[string]dbrole.DbRole{
		"service_auditor": dbrole.READER,
		"service_admin":   dbrole.WRITER,
	}
	_ = myProtostore.Register(context.TODO(), roleMappingForMemory, &pb.Memory{})

	// Store protobuf message using Upsert
	id := "0001"
	memory := &pb.Memory{
		Brand: "Samsung",
		Size:  32,
		Speed: 2933,
		Type:  "DDR4",
	}
	rowsAffected, metadata, err := myProtostore.Upsert(ctx, id, memory)
	fmt.Println("After Upsert::", "revision:", metadata.Revision, "rowsAffected:", rowsAffected, "err:", err)

	// Retrieve protobuf message using ID
	memory = &pb.Memory{}
	err = myProtostore.FindById(ctx, id, memory, &metadata)
	fmt.Println("FindById::", "revision:", metadata.Revision, "rowsAffected:", rowsAffected, "err:", err)

	// Update the protobuf message with metadata (existing revision)
	memory.Speed++
	rowsAffected, metadata, err = myProtostore.UpdateWithMetadata(ctx, id, memory, metadata)
	fmt.Println("After UpdateWithMetadata::", "revision:", metadata.Revision, "rowsAffected:", rowsAffected, "err:", err)

	// Retrieve all the protobuf messages
	queryResults := make([]*pb.Memory, 0)
	metadataMap, err := myProtostore.FindAll(ctx, &queryResults, datastore.NoPagination())
	fmt.Println("FindAll::", "revision:", metadataMap[id].Revision, "rowsFound:", len(queryResults), "err:", err)

	// Delete the protobuf (soft delete)
	rowsAffected, _, err = myProtostore.SoftDeleteById(ctx, id, &pb.Memory{})
	fmt.Println("SoftDeleteById::", "rowsAffected:", rowsAffected, "err:", err)

	// Delete the protobuf (full delete)
	rowsAffected, err = myProtostore.DeleteById(ctx, id, &pb.Memory{})
	fmt.Println("DeleteById::", "rowsAffected:", rowsAffected, "err:", err)

}

Output

After Upsert:: revision: 1 rowsAffected: 1 err: <nil>
FindById:: revision: 1 rowsAffected: 1 err: <nil>
After UpdateWithMetadata:: revision: 2 rowsAffected: 1 err: <nil>
FindAll:: revision: 2 rowsFound: 1 err: <nil>
SoftDeleteById:: rowsAffected: 1 err: <nil>
DeleteById:: rowsAffected: 1 err: <nil>

func GetProtoStore(logger *logrus.Entry, ds datastore.DataStore) ProtoStore

type ProtoStoreMsg struct {
    Id         string         `gorm:"primaryKey" json:"id"`
    Msg        []byte         `json:"-"`
    ParentId   string         `json:"parent_id,omitempty"`
    Revision   int64          `json:"revision"`
    OrgId      string         `gorm:"primaryKey" json:"org_id"`
    InstanceId string         `gorm:"primaryKey" json:"instance_id,omitempty"`
    CreatedAt  time.Time      `json:"-"`
    UpdatedAt  time.Time      `json:"-"`
    DeletedAt  gorm.DeletedAt `json:"-"`
    XTableName string         `gorm:"-" json:"x_table_name"`
}

func (*ProtoStoreMsg) String

func (msg *ProtoStoreMsg) String() string

func (*ProtoStoreMsg) TableName

func (msg *ProtoStoreMsg) TableName() string

type ProtobufDataStore struct {
    // contains filtered or unexported fields
}

func (ProtobufDataStore) DeleteById

func (p ProtobufDataStore) DeleteById(ctx context.Context, id string, msg proto.Message) (int64, error)

func (ProtobufDataStore) DropTables

func (p ProtobufDataStore) DropTables(msgs ...proto.Message) error

func (ProtobufDataStore) FindAll

func (p ProtobufDataStore) FindAll(ctx context.Context, msgs interface{}, pagination *datastore.Pagination) (metadataMap map[string]Metadata, err error)

FindAll Finds all messages (of the same type as the element of msgs) in Protostore and stores the result in msgs. msgs must be a pointer to a slice of Protobuf structs or a pointer to a slice of pointers to Protobuf structs. It will be modified in-place. Returns a map of Protobuf messages' IDs to their metadata (parent ID & revision).

func (ProtobufDataStore) FindAllAsMap

func (p ProtobufDataStore) FindAllAsMap(ctx context.Context, msgsMap interface{}, pagination *datastore.Pagination) (metadataMap map[string]Metadata, err error)

func (ProtobufDataStore) FindAllIncludingSoftDeleted

func (p ProtobufDataStore) FindAllIncludingSoftDeleted(ctx context.Context, msgs interface{}, pagination *datastore.Pagination) (metadataMap map[string]Metadata, err error)

func (ProtobufDataStore) FindById

func (p ProtobufDataStore) FindById(ctx context.Context, id string, msg proto.Message, metadata *Metadata) error

Finds a Protobuf message by ID. If metadata arg. is non-nil, fills it with the metadata (parent ID & revision) of the Protobuf message that was found.

func (ProtobufDataStore) FindByIdIncludingSoftDeleted

func (p ProtobufDataStore) FindByIdIncludingSoftDeleted(ctx context.Context, id string, msg proto.Message, metadata *Metadata) error

func (ProtobufDataStore) GetAuthorizer

func (p ProtobufDataStore) GetAuthorizer() authorizer.Authorizer

func (ProtobufDataStore) GetDataStore

func (p ProtobufDataStore) GetDataStore() datastore.DataStore

func (ProtobufDataStore) GetMetadata

func (p ProtobufDataStore) GetMetadata(ctx context.Context, id string, msg proto.Message) (md Metadata, err error)

func (ProtobufDataStore) GetRevision

func (p ProtobufDataStore) GetRevision(ctx context.Context, id string, msg proto.Message) (int64, error)

func (ProtobufDataStore) GetSoftDeletedMetadata

func (p ProtobufDataStore) GetSoftDeletedMetadata(ctx context.Context, id string, msg proto.Message) (md Metadata, err error)

func (ProtobufDataStore) Insert

func (p ProtobufDataStore) Insert(ctx context.Context, id string, msg proto.Message) (rowsAffected int64, md Metadata, err error)

@DEPRECATED See [InsertWithMetadata].

func (ProtobufDataStore) InsertWithMetadata

func (p ProtobufDataStore) InsertWithMetadata(ctx context.Context, id string, msg proto.Message, metadata Metadata) (rowsAffected int64, md Metadata, err error)

Inserts a new Protobuf record in the DB. Returns, rowsAffected - 0 if insertion fails; 1 otherwise md - metadata of the new Protobuf record err - error that occurred during insertion, if any.

func (ProtobufDataStore) MsgToFilter

func (p ProtobufDataStore) MsgToFilter(ctx context.Context, id string, msg proto.Message) (pMsg *ProtoStoreMsg, err error)

Return the ProtoStoreMsg that can be used for filtering with id/orgId filled up and error that occurred during extraction orgId from context.

func (ProtobufDataStore) MsgToPersist

func (p ProtobufDataStore) MsgToPersist(ctx context.Context, id string, msg proto.Message, md Metadata) (pMsg *ProtoStoreMsg, err error)

Return the serialized ProtoStoreMsg that can be persisted to database and error that occurred during extraction orgId from context, or serialization.

func (ProtobufDataStore) Register

func (p ProtobufDataStore) Register(ctx context.Context, roleMapping map[string]dbrole.DbRole, msgs ...proto.Message) error

func (ProtobufDataStore) SoftDeleteById

func (p ProtobufDataStore) SoftDeleteById(ctx context.Context, id string, msg proto.Message) (int64, Metadata, error)

func (ProtobufDataStore) Update

func (p ProtobufDataStore) Update(ctx context.Context, id string, msg proto.Message) (rowsAffected int64, md Metadata, err error)

Update Fetches metadata for the record and updates the Protobuf message. NOTE: Avoid using this method in user-workflows and only in service-to-service workflows when the updates are already ordered by some other service/app.

func (ProtobufDataStore) UpdateWithMetadata

func (p ProtobufDataStore) UpdateWithMetadata(ctx context.Context, id string, msg proto.Message, metadata Metadata) (rowsAffected int64, md Metadata, err error)

Updates an existing Protobuf record in the DB. Returns, rowsAffected - 0 if update fails; 1 otherwise md - metadata of the updated Protobuf record err - error that occurred during update, if any.

func (ProtobufDataStore) Upsert

func (p ProtobufDataStore) Upsert(ctx context.Context, id string, msg proto.Message) (rowsAffected int64, md Metadata, err error)

Upsert Fetches metadata for the record and upserts the Protobuf message. NOTE: Avoid using this method in user-workflows and only in service-to-service workflows when the updates are already ordered by some other service/app.

func (ProtobufDataStore) UpsertWithMetadata

func (p ProtobufDataStore) UpsertWithMetadata(ctx context.Context, id string, msg proto.Message, metadata Metadata) (rowsAffected int64, md Metadata, err error)

Upserts a Protobuf record in the DB (if the record exists, it is updated; if it does not, it is inserted). Returns, rowsAffected - 0 if upsert fails; 1 otherwise md - metadata of the upserted Protobuf record err - error that occurred during upsert, if any.

realization_store

import "github.com/vmware-labs/multi-tenant-persistence-for-saas/pkg/realization_store"

This package exposes IRealizationStore interface that will be used to support managing statuses of resources being realized at different enforcement points and aggregating it OverallStatus to represent the state of resource.

## Implementation

Two database tables will be used to track realization status of each resource type. The first one, `EnforcementStatus`, stores realization status of a resource at specific enforcement points, where an enforcement point is a workload where a resource is actually realized and where its status can be queried. The second one, `OverallStatus`, stores overall realization status of a resource across all enforcement points. The following ER diagrams show the DB schema for resource as an example.

### ER diagram

```mermaid erDiagram

Resource ||--|| ResourceOverallStatus : "Overall Status"
Resource ||--o{ ResourceEnforcementStatus : "Status Per Enforcement Point"

Resource {
    string id
    string org_id
    byte[] msg
    string parent_id
    bigint revision
}

ResourceOverallStatus {
    string id
    string org_id
    enum status "DELETION_REALIZED|REALIZED|DELETION_IN_PROGRESS|DELETION_PENDING|IN_PROGRESS|PENDING|ERROR"
    string additional_details
    created_at timestamptz
    updated_at timestamptz
    bigint revision
}

ResourceEnforcementStatus {
    string id
    string org_id
    string enforcement_point_id
    enum status  "DELETION_REALIZED|REALIZED|DELETION_IN_PROGRESS|DELETION_PENDING|IN_PROGRESS|PENDING|ERROR"
    string additional_details
    created_at timestamptz
    updated_at timestamptz
    bigint revision
}

```

## Common workflows

The common workflows related to realization status support would be these: **Scenario: A resource is created** End user issues a request to persist an intent for resource _R1_. It needs to be realized at enforcement points _E1_ & _E2_. Consumer(s) would call these methods:

PersistIntent(R1)
MarkEnforcementAsPending(E1, R1)
MarkEnforcementAsPending(E2, R1)
//R1 is successfully realized at E1
MarkEnforcementAsSuccess(E1, R1)
//R1 is successfully realized at E2
MarkEnforcementAsSuccess(E2, R1)

**Scenario: A resource is modified. Its status is queried** End user issues a request to perist a modified intent for resource _R1_. The modified resource needs to be realized at enforcement points _E1_ & _E2_. Consumer(s) would call these methods:

PersistIntent(R1)
MarkEnforcementAsPending(E1, R1)
MarkEnforcementAsPending(E2, R1)
//R1 is successfully realized at E1
MarkEnforcementAsSuccess(E1, R1)
//R1 is successfully realized at E2
MarkEnforcementAsSuccess(E2, R1)
GetOverallStatusWithEnforcementDetails(R1)

**Scenario: A resource is deleted** End user issues a request to delete an intent for resource _R1_. The deleted resource needs to be "unenforced" at enforcement points _E1_ & _E2_. Consumer(s) would call these methods:

Delete(R1)
MarkEnforcementAsDeletionPending(E1, R1)
MarkEnforcementAsDeletionPending(E1, R1)
//R1 is successfully "unenforced" at E1
MarkEnforcementAsDeletionRealized(E1, R1) //TBD
//R1 is successfully "unenforced" at E2
MarkEnforcementAsDeletionRealized(E2, R1) //TBD
//Cleanup mechanism deletes stale status records for deleted resources - TBD

**Scenario: A resource is created but fails to be realized at some enforcement points** End user issues a request to persist an intent for resource _R1_. It needs to be realized at enforcement points _E1_ & _E2_. Consumer(s) would call these methods:

PersistIntent(R1)
MarkEnforcementAsPending(E1, R1)
MarkEnforcementAsPending(E2, R1)
//R1 fails to be realized at E1 due to error err
MarkEnforcementAsError(E1, err, R1)
//R1 is successfully realized at E2
MarkEnforcementAsSuccess(E2, R1)

**Scenario: A new enforcement point is added** An enforcement point _E3_ is added (by end-user or an admin). Resources _R1_ and _R2_ need to be enforced on it. Consumer(s) would call these methods:

//Enforcement point is added
MarkEnforcementAsPending(E3, R1, R2)
//R1 is successfully realized at E3
MarkEnforcementAsSuccess(E3, R1)
//R2 is successfully realized at E3
MarkEnforcementAsSuccess(E3, R2)

**Scenario: An enforcement point is removed** An enforcement point _E3_ is added (by end-user or an admin). Resources _R1_ and _R2_ need to be "unenforced" on it. Consumer(s) would call these methods:

//Enforcement point is removed
MarkEnforcementAsDeletionRealized(E3, R1, R2) //TBD

Index

Constants

Constants for logger field names & values.

const (
    ORG_ID            = "orgId"
    RESOURCE_ID       = "resourceId"
    ENFORCEMENT_POINT = "enforcementPoint"
    ALL               = "*"
)

Logging statements.

const (
    RESOURCE           = "resource"
    OVERALL_STATUS     = "overall status"
    ENFORCEMENT_STATUS = "enforcement status"
    ENFORCEMENT        = "enforcement"
    INTENT             = "intent"

    PERSISTING    = "Persisting"
    SETTING       = "Setting"
    RESETTING     = "Resetting"
    SOFT_DELETING = "Soft-Deleting"
    DELETING      = "Deleting"
    PURGING       = "Purging"
    FETCHING      = "Fetching"
    REGISTERING   = "Registering"
    MARKING       = "Marking"

    STARTED  = "..."
    FAILED   = "failed: "
    ERRORED  = "errored: "
    FINISHED = "finished."

    AS_PENDING           = "as pending"
    AS_SUCCESS           = "as success"
    AS_ERROR             = "as error"
    AS_DELETION_PENDING  = "as deletion pending"
    AS_DELETION_REALIZED = "as deletion realized"
)

const (
    ADDITIONAL_DETAILS_LENGTH_CAP = 1024
)

func GetEnforcementStatusTableName(msg proto.Message) string

func GetOverallStatusTableName(msg proto.Message) string

type EnforcementStatus struct {
    Id                 string `gorm:"primaryKey"`
    OrgId              string `gorm:"primaryKey"`
    EnforcementPointId string `gorm:"primaryKey"`
    RealizationStatus  Status
    AdditionalDetails  string
    Revision           int64  `gorm:"column:resource_revision"`
    XTableName         string `gorm:"-"`
    CreatedAt          time.Time
    UpdatedAt          time.Time
}

func GetModelEnforcementStatusRecord(resource *ProtobufWithMetadata, orgId string) *EnforcementStatus

func GetModelEnforcementStatusRecordWithoutRevision(resource *ProtobufWithMetadata, orgId string) *EnforcementStatus

func (*EnforcementStatus) TableName

func (e *EnforcementStatus) TableName() string

type IRealizationStore interface {
    Register(ctx context.Context, roleMapping map[string]dbrole.DbRole, msgs ...proto.Message) error

    FindById(ctx context.Context, id string, resource *ProtobufWithMetadata) error

    PersistIntent(ctx context.Context, resource *ProtobufWithMetadata) (rowsAffected int64, metadata protostore.Metadata, err error)
    MarkEnforcementAsPending(ctx context.Context, enforcementPoint string, resources ...*ProtobufWithMetadata) error
    MarkEnforcementAsInProgress(ctx context.Context, enforcementPoint string, resources ...*ProtobufWithMetadata) error
    MarkEnforcementAsSuccess(ctx context.Context, enforcementPoint string, resources ...*ProtobufWithMetadata) error
    MarkEnforcementAsError(ctx context.Context, enforcementPoint string, errStr string, resources ...*ProtobufWithMetadata) error

    SoftDelete(ctx context.Context, resource *ProtobufWithMetadata) (rowsAffected int64, metadata protostore.Metadata, err error)
    Delete(ctx context.Context, resource *ProtobufWithMetadata) (rowsAffected int64, err error)
    Purge(ctx context.Context, resource *ProtobufWithMetadata) (rowsAffected int64, err error)
    MarkEnforcementAsDeletionPending(ctx context.Context, enforcementPoint string, resources ...*ProtobufWithMetadata) error
    MarkEnforcementAsDeletionInProgress(ctx context.Context, enforcementPoint string, resources ...*ProtobufWithMetadata) error
    MarkEnforcementAsDeletionRealized(ctx context.Context, enforcementPoint string, resources ...*ProtobufWithMetadata) error
    MarkEnforcementAsDeletionError(ctx context.Context, enforcementPoint string, errStr string, resources ...*ProtobufWithMetadata) error

    GetOverallStatus(ctx context.Context, resource *ProtobufWithMetadata) (Status, error)
    GetOverallStatusWithEnforcementDetails(ctx context.Context, resource *ProtobufWithMetadata) (Status, map[string]Status, error)
    GetEnforcementStatusMap(ctx context.Context, resource *ProtobufWithMetadata) (overallStatus OverallStatus, enforcementStatusMap map[string]EnforcementStatus, err error)

    // To handle error scenarios and for intrumentation cases
    PurgeStaleStatusRecords(ctx context.Context, resource *ProtobufWithMetadata) (rowsAffected int64, err error)
    PurgeStaleEnforcementStatusRecords(ctx context.Context, resource *ProtobufWithMetadata, enforcementStatusRecordMap map[string]EnforcementStatus) (rowsAffected int64, err error)
}

func GetRealizationStore(d datastore.DataStore, p protostore.ProtoStore, logger *logrus.Entry) IRealizationStore

type OverallStatus struct {
    Id                string `gorm:"primaryKey"`
    OrgId             string `gorm:"primaryKey"`
    RealizationStatus Status
    AdditionalDetails string
    Revision          int64  `gorm:"column:resource_revision"`
    XTableName        string `gorm:"-"`
    CreatedAt         time.Time
    UpdatedAt         time.Time
}

func GetModelOverallStatusRecord(resource *ProtobufWithMetadata, orgId string) *OverallStatus

func (*OverallStatus) TableName

func (o *OverallStatus) TableName() string

type ProtobufWithMetadata struct {
    proto.Message
    protostore.Metadata
}

type Status

type Status int

The values are ordered in th priority order, where the highest value is set as overall status for given resource, from the list of enforcement statuses for that resource NOTE: Do not reorder or change the values of the statuses.

const (
    UNKNOWN              Status = 0
    DELETION_REALIZED    Status = 100
    REALIZED             Status = 200
    DELETION_IN_PROGRESS Status = 300
    DELETION_PENDING     Status = 400
    IN_PROGRESS          Status = 600
    PENDING              Status = 800
    ERROR                Status = 1000
)

func (Status) String

func (i Status) String() string

Generated by gomarkdoc