github.com/kyma-project/kyma-environment-broker@v0.0.1/internal/storage/driver/memory/instance.go (about)

     1  package memory
     2  
     3  import (
     4  	"database/sql"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"sort"
     9  	"sync"
    10  
    11  	"github.com/kyma-project/kyma-environment-broker/common/pagination"
    12  	"github.com/kyma-project/kyma-environment-broker/internal"
    13  	"github.com/kyma-project/kyma-environment-broker/internal/storage/dberr"
    14  	"github.com/kyma-project/kyma-environment-broker/internal/storage/dbmodel"
    15  	"github.com/kyma-project/kyma-environment-broker/internal/storage/predicate"
    16  	"github.com/pivotal-cf/brokerapi/v8/domain"
    17  )
    18  
    19  type instances struct {
    20  	mu                sync.Mutex
    21  	instances         map[string]internal.Instance
    22  	operationsStorage *operations
    23  }
    24  
    25  func NewInstance(operations *operations) *instances {
    26  	return &instances{
    27  		instances:         make(map[string]internal.Instance, 0),
    28  		operationsStorage: operations,
    29  	}
    30  }
    31  
    32  func (s *instances) InsertWithoutEncryption(instance internal.Instance) error {
    33  	return errors.New("not implemented")
    34  }
    35  func (s *instances) UpdateWithoutEncryption(instance internal.Instance) (*internal.Instance, error) {
    36  	return nil, errors.New("not implemented")
    37  }
    38  func (s *instances) ListWithoutDecryption(dbmodel.InstanceFilter) ([]internal.Instance, int, int, error) {
    39  	return nil, 0, 0, errors.New("not implemented")
    40  }
    41  
    42  func (s *instances) FindAllJoinedWithOperations(prct ...predicate.Predicate) ([]internal.InstanceWithOperation, error) {
    43  	var instances []internal.InstanceWithOperation
    44  
    45  	// simulate left join without grouping on column
    46  	for id, v := range s.instances {
    47  		dOps, dErr := s.operationsStorage.ListDeprovisioningOperationsByInstanceID(id)
    48  		if dErr != nil && !dberr.IsNotFound(dErr) {
    49  			return nil, dErr
    50  		}
    51  		pOps, pErr := s.operationsStorage.ListProvisioningOperationsByInstanceID(id)
    52  		if pErr != nil && !dberr.IsNotFound(pErr) {
    53  			return nil, pErr
    54  		}
    55  		uOps, uErr := s.operationsStorage.ListUpgradeKymaOperationsByInstanceID(id)
    56  		if uErr != nil && !dberr.IsNotFound(uErr) {
    57  			return nil, uErr
    58  		}
    59  
    60  		if !dberr.IsNotFound(dErr) {
    61  			for _, op := range dOps {
    62  				instances = append(instances, internal.InstanceWithOperation{
    63  					Instance:       v,
    64  					Type:           sql.NullString{String: string(internal.OperationTypeDeprovision), Valid: true},
    65  					State:          sql.NullString{String: string(op.State), Valid: true},
    66  					Description:    sql.NullString{String: op.Description, Valid: true},
    67  					OpCreatedAt:    op.CreatedAt,
    68  					IsSuspensionOp: op.Temporary,
    69  				})
    70  			}
    71  		}
    72  
    73  		if !dberr.IsNotFound(pErr) {
    74  			for _, op := range pOps {
    75  				instances = append(instances, internal.InstanceWithOperation{
    76  					Instance:       v,
    77  					Type:           sql.NullString{String: string(internal.OperationTypeProvision), Valid: true},
    78  					State:          sql.NullString{String: string(op.State), Valid: true},
    79  					Description:    sql.NullString{String: op.Description, Valid: true},
    80  					OpCreatedAt:    op.CreatedAt,
    81  					IsSuspensionOp: false,
    82  				})
    83  			}
    84  		}
    85  
    86  		if !dberr.IsNotFound(uErr) {
    87  			for _, op := range uOps {
    88  				instances = append(instances, internal.InstanceWithOperation{
    89  					Instance:    v,
    90  					Type:        sql.NullString{String: string(internal.OperationTypeUpgradeKyma), Valid: true},
    91  					State:       sql.NullString{String: string(op.State), Valid: true},
    92  					Description: sql.NullString{String: op.Description, Valid: true},
    93  				})
    94  			}
    95  		}
    96  
    97  		if dberr.IsNotFound(dErr) && dberr.IsNotFound(pErr) {
    98  			instances = append(instances, internal.InstanceWithOperation{Instance: v})
    99  		}
   100  	}
   101  
   102  	for _, p := range prct {
   103  		p.ApplyToInMemory(instances)
   104  	}
   105  
   106  	return instances, nil
   107  }
   108  
   109  func (s *instances) FindAllInstancesForRuntimes(runtimeIdList []string) ([]internal.Instance, error) {
   110  	var instances []internal.Instance
   111  
   112  	for _, runtimeID := range runtimeIdList {
   113  		for _, inst := range s.instances {
   114  			if inst.RuntimeID == runtimeID {
   115  				instances = append(instances, inst)
   116  			}
   117  		}
   118  	}
   119  
   120  	if len(instances) == 0 {
   121  		return nil, dberr.NotFound("instances with runtime id from list %+q not exist", runtimeIdList)
   122  	}
   123  
   124  	return instances, nil
   125  }
   126  
   127  func (s *instances) FindAllInstancesForSubAccounts(subAccountslist []string) ([]internal.Instance, error) {
   128  	var instances []internal.Instance
   129  
   130  	for _, subAccount := range subAccountslist {
   131  		for _, inst := range s.instances {
   132  			if inst.SubAccountID == subAccount {
   133  				instances = append(instances, inst)
   134  			}
   135  		}
   136  	}
   137  
   138  	return instances, nil
   139  }
   140  
   141  func (s *instances) GetNumberOfInstancesForGlobalAccountID(globalAccountID string) (int, error) {
   142  	numberOfInstances := 0
   143  	for _, inst := range s.instances {
   144  		if inst.GlobalAccountID == globalAccountID && inst.DeletedAt.IsZero() {
   145  			numberOfInstances++
   146  		}
   147  	}
   148  	return numberOfInstances, nil
   149  }
   150  
   151  func (s *instances) GetByID(instanceID string) (*internal.Instance, error) {
   152  	inst, ok := s.instances[instanceID]
   153  	if !ok {
   154  		return nil, dberr.NotFound("instance with id %s not exist", instanceID)
   155  	}
   156  
   157  	// In database instance details are marshalled and kept as strings.
   158  	// If marshaling is ommited below, fields with `json:"-"` are never cleared
   159  	// when stored in memory db. Marshaling in the current contenxt allows for
   160  	// memory db to behave similary to production env.
   161  	marshaled, err := json.Marshal(inst)
   162  	unmarshaledInstance := internal.Instance{}
   163  	err = json.Unmarshal(marshaled, &unmarshaledInstance)
   164  
   165  	op, err := s.operationsStorage.GetLastOperation(instanceID)
   166  	if err != nil {
   167  		if dberr.IsNotFound(err) {
   168  			return &inst, nil
   169  		}
   170  		return nil, err
   171  	}
   172  
   173  	detailsMarshaled, err := json.Marshal(op.InstanceDetails)
   174  	detailsUnmarshaled := internal.InstanceDetails{}
   175  	err = json.Unmarshal(detailsMarshaled, &detailsUnmarshaled)
   176  	unmarshaledInstance.InstanceDetails = detailsUnmarshaled
   177  
   178  	return &unmarshaledInstance, nil
   179  }
   180  
   181  func (s *instances) Delete(instanceID string) error {
   182  	s.mu.Lock()
   183  	defer s.mu.Unlock()
   184  
   185  	delete(s.instances, instanceID)
   186  	return nil
   187  }
   188  
   189  func (s *instances) Insert(instance internal.Instance) error {
   190  	s.mu.Lock()
   191  	defer s.mu.Unlock()
   192  	s.instances[instance.InstanceID] = instance
   193  
   194  	return nil
   195  }
   196  
   197  func (s *instances) Update(instance internal.Instance) (*internal.Instance, error) {
   198  	s.mu.Lock()
   199  	defer s.mu.Unlock()
   200  	oldInst, exists := s.instances[instance.InstanceID]
   201  	if !exists {
   202  		return nil, dberr.NotFound("instance %s not found", instance.InstanceID)
   203  	}
   204  	if oldInst.Version != instance.Version {
   205  		return nil, dberr.Conflict("unable to update instance %s - conflict", instance.InstanceID)
   206  	}
   207  	instance.Version = instance.Version + 1
   208  	s.instances[instance.InstanceID] = instance
   209  
   210  	return &instance, nil
   211  }
   212  
   213  func (s *instances) GetInstanceStats() (internal.InstanceStats, error) {
   214  	return internal.InstanceStats{}, fmt.Errorf("not implemented")
   215  }
   216  
   217  func (s *instances) GetERSContextStats() (internal.ERSContextStats, error) {
   218  	return internal.ERSContextStats{}, fmt.Errorf("not implemented")
   219  }
   220  
   221  func (s *instances) List(filter dbmodel.InstanceFilter) ([]internal.Instance, int, int, error) {
   222  	s.mu.Lock()
   223  	defer s.mu.Unlock()
   224  	var toReturn []internal.Instance
   225  
   226  	offset := pagination.ConvertPageAndPageSizeToOffset(filter.PageSize, filter.Page)
   227  
   228  	instances := s.filterInstances(filter)
   229  	sortInstancesByCreatedAt(instances)
   230  
   231  	for i := offset; (filter.PageSize < 1 || i < offset+filter.PageSize) && i < len(instances); i++ {
   232  		toReturn = append(toReturn, s.instances[instances[i].InstanceID])
   233  	}
   234  
   235  	return toReturn,
   236  		len(toReturn),
   237  		len(instances),
   238  		nil
   239  }
   240  
   241  func sortInstancesByCreatedAt(instances []internal.Instance) {
   242  	sort.Slice(instances, func(i, j int) bool {
   243  		return instances[i].CreatedAt.Before(instances[j].CreatedAt)
   244  	})
   245  }
   246  
   247  func (s *instances) filterInstances(filter dbmodel.InstanceFilter) []internal.Instance {
   248  	inst := make([]internal.Instance, 0, len(s.instances))
   249  	var ok bool
   250  	equal := func(a, b string) bool {
   251  		return a == b
   252  	}
   253  	shootMatch := func(shootName, filter string) bool {
   254  		return shootName == filter
   255  	}
   256  
   257  	for _, v := range s.instances {
   258  		if ok = matchFilter(v.InstanceID, filter.InstanceIDs, equal); !ok {
   259  			continue
   260  		}
   261  		if ok = matchFilter(v.GlobalAccountID, filter.GlobalAccountIDs, equal); !ok {
   262  			continue
   263  		}
   264  		if ok = matchFilter(v.SubscriptionGlobalAccountID, filter.SubscriptionGlobalAccountIDs, equal); !ok {
   265  			continue
   266  		}
   267  		if ok = matchFilter(v.SubAccountID, filter.SubAccountIDs, equal); !ok {
   268  			continue
   269  		}
   270  		if ok = matchFilter(v.RuntimeID, filter.RuntimeIDs, equal); !ok {
   271  			continue
   272  		}
   273  		if ok = matchFilter(v.ServicePlanName, filter.Plans, equal); !ok {
   274  			continue
   275  		}
   276  		if ok = matchFilter(v.ProviderRegion, filter.Regions, equal); !ok {
   277  			continue
   278  		}
   279  		if len(filter.Shoots) > 0 {
   280  			// required for shootName
   281  			lastOp, _ := s.operationsStorage.GetLastOperation(v.InstanceID)
   282  			if ok = matchFilter(lastOp.ShootName, filter.Shoots, shootMatch); !ok {
   283  				continue
   284  			}
   285  		}
   286  		if ok = s.matchInstanceState(v.InstanceID, filter.States); !ok {
   287  			continue
   288  		}
   289  
   290  		inst = append(inst, v)
   291  	}
   292  
   293  	return inst
   294  }
   295  
   296  func matchFilter(value string, filters []string, match func(string, string) bool) bool {
   297  	if len(filters) == 0 {
   298  		return true
   299  	}
   300  	for _, f := range filters {
   301  		if match(value, f) {
   302  			return true
   303  		}
   304  	}
   305  	return false
   306  }
   307  
   308  func (s *instances) matchInstanceState(instanceID string, states []dbmodel.InstanceState) bool {
   309  	if len(states) == 0 {
   310  		return true
   311  	}
   312  	op, err := s.operationsStorage.GetLastOperation(instanceID)
   313  	if err != nil {
   314  		// To support instance test cases without any operations
   315  		return true
   316  	}
   317  
   318  	for _, s := range states {
   319  		switch s {
   320  		case dbmodel.InstanceSucceeded:
   321  			if op.State == domain.Succeeded && op.Type != internal.OperationTypeDeprovision {
   322  				return true
   323  			}
   324  		case dbmodel.InstanceFailed:
   325  			if op.State == domain.Failed && (op.Type == internal.OperationTypeProvision || op.Type == internal.OperationTypeDeprovision) {
   326  				return true
   327  			}
   328  		case dbmodel.InstanceError:
   329  			if op.State == domain.Failed && op.Type != internal.OperationTypeProvision && op.Type != internal.OperationTypeDeprovision {
   330  				return true
   331  			}
   332  		case dbmodel.InstanceProvisioning:
   333  			if op.Type == internal.OperationTypeProvision && op.State == domain.InProgress {
   334  				return true
   335  			}
   336  		case dbmodel.InstanceDeprovisioning:
   337  			if op.Type == internal.OperationTypeDeprovision && op.State == domain.InProgress {
   338  				return true
   339  			}
   340  		case dbmodel.InstanceUpgrading:
   341  			if (op.Type == internal.OperationTypeUpgradeKyma || op.Type == internal.OperationTypeUpgradeCluster) && op.State == domain.InProgress {
   342  				return true
   343  			}
   344  		case dbmodel.InstanceUpdating:
   345  			if op.Type == internal.OperationTypeUpdate && op.State == domain.InProgress {
   346  				return true
   347  			}
   348  		case dbmodel.InstanceDeprovisioned:
   349  			if op.State == domain.Succeeded && op.Type == internal.OperationTypeDeprovision {
   350  				return true
   351  			}
   352  		case dbmodel.InstanceNotDeprovisioned:
   353  			if !(op.State == domain.Succeeded && op.Type == internal.OperationTypeDeprovision) {
   354  				return true
   355  			}
   356  		}
   357  	}
   358  
   359  	return false
   360  }