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 }