github.com/Axway/agent-sdk@v1.1.101/pkg/migrate/apisimigration.go (about) 1 package migrate 2 3 import ( 4 "context" 5 "regexp" 6 "strconv" 7 8 apiv1 "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/api/v1" 9 management "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/management/v1alpha1" 10 "github.com/Axway/agent-sdk/pkg/apic/definitions" 11 "github.com/Axway/agent-sdk/pkg/config" 12 "github.com/Axway/agent-sdk/pkg/util" 13 "github.com/Axway/agent-sdk/pkg/util/log" 14 ) 15 16 // APISIMigration - used for migrating API Service Instances 17 type APISIMigration struct { 18 migration 19 logger log.FieldLogger 20 } 21 22 // NewAPISIMigration creates a new APISIMigration 23 func NewAPISIMigration(client client, cfg config.CentralConfig) *APISIMigration { 24 return &APISIMigration{ 25 migration: migration{ 26 client: client, 27 cfg: cfg, 28 }, 29 logger: log.NewFieldLogger(). 30 WithPackage("sdk.migrate"). 31 WithComponent("instance-migration"), 32 } 33 } 34 35 // Migrate checks an APIServiceInstance for the "scopes" key in the schema, and removes it if it is found. 36 func (m *APISIMigration) Migrate(ctx context.Context, ri *apiv1.ResourceInstance) (*apiv1.ResourceInstance, error) { 37 if ri.Kind != management.APIServiceGVK().Kind { 38 return ri, nil 39 } 40 logger := log.UpdateLoggerWithContext(ctx, m.logger) 41 logger.Trace("api service migration") 42 43 // skip migration if instance migration is not enabled 44 if !m.cfg.GetMigrationSettings().ShouldCleanInstances() { 45 return ri, nil 46 } 47 48 if isMigrationCompleted(ri, definitions.InstanceMigration) { 49 // migration ran already 50 logger.Debugf("service instance migration already completed") 51 return ri, nil 52 } 53 54 instances, _ := m.getInstances(ri) 55 logger.WithField("instances", instances).Debug("all instances") 56 if err := m.cleanInstances(ctx, instances); err != nil { 57 return ri, err 58 } 59 60 // mark the migration as complete 61 util.SetAgentDetailsKey(ri, definitions.InstanceMigration, definitions.MigrationCompleted) 62 return ri, m.updateRI(ri) 63 } 64 65 // getInstances gets a list of instances for the service 66 func (m *APISIMigration) getInstances(ri *apiv1.ResourceInstance) ([]*apiv1.ResourceInstance, error) { 67 url := m.cfg.GetInstancesURL() 68 q := map[string]string{ 69 "query": queryFuncByMetadataID(ri.Metadata.ID), 70 } 71 72 return m.getAllRI(url, q) 73 } 74 75 func (m *APISIMigration) cleanInstances(ctx context.Context, instances []*apiv1.ResourceInstance) error { 76 logger := log.NewLoggerFromContext(ctx) 77 78 logger.Tracef("cleaning instances") 79 type instanceNameIndex struct { 80 ri *apiv1.ResourceInstance 81 index int 82 } 83 84 re := regexp.MustCompile(`([-\.a-z0-9]*)\.([0-9]*$)`) 85 86 // sort all instances into buckets based on name, removing any index number, noting the highest 87 toKeep := map[string]instanceNameIndex{} 88 for _, inst := range instances { 89 logger := logger.WithField(string(management.APIServiceInstanceCtx), inst.Name) 90 logger.Tracef("handling instances") 91 name := inst.Name 92 result := re.FindAllStringSubmatch(name, -1) 93 group := name 94 index := 0 95 var err error 96 if len(result) > 0 { 97 group = result[0][1] 98 index, err = strconv.Atoi(result[0][2]) 99 if err != nil { 100 return err 101 } 102 } 103 logger = logger.WithField("service-group", group).WithField("instance-index", index) 104 logger.Tracef("parsed instance name") 105 106 keepIndex := -1 107 if i, ok := toKeep[group]; ok { 108 keepIndex = i.index 109 } 110 111 thisNameIndex := instanceNameIndex{ 112 ri: inst, 113 index: index, 114 } 115 116 if keepIndex == -1 { 117 logger.Trace("first instance in group") 118 toKeep[group] = thisNameIndex 119 } else if keepIndex < index { 120 logger.Tracef("removing previous high instance with name: %s", toKeep[group].ri.Name) 121 m.client.DeleteResourceInstance(toKeep[group].ri) 122 toKeep[group] = thisNameIndex 123 } else { 124 logger.Tracef("removing this instance") 125 m.client.DeleteResourceInstance(thisNameIndex.ri) 126 } 127 } 128 129 return nil 130 }