github.com/Axway/agent-sdk@v1.1.101/pkg/migrate/attributemigration.go (about) 1 package migrate 2 3 import ( 4 "context" 5 "fmt" 6 "regexp" 7 "sync" 8 9 apiv1 "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/api/v1" 10 management "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/management/v1alpha1" 11 defs "github.com/Axway/agent-sdk/pkg/apic/definitions" 12 "github.com/Axway/agent-sdk/pkg/config" 13 "github.com/Axway/agent-sdk/pkg/util" 14 "github.com/Axway/agent-sdk/pkg/util/log" 15 ) 16 17 var oldAttrs = []string{ 18 defs.AttrPreviousAPIServiceRevisionID, 19 defs.AttrExternalAPIID, 20 defs.AttrExternalAPIPrimaryKey, 21 defs.AttrExternalAPIName, 22 defs.AttrExternalAPIStage, 23 defs.AttrCreatedBy, 24 } 25 26 var regexes = make([]string, 0) 27 28 var tagRegexes = make([]string, 0) 29 30 // MatchAttrPattern matches attribute patterns to match against an attribute to migrate to the x-agent-details subresource 31 func MatchAttrPattern(pattern ...string) { 32 regexes = append(regexes, pattern...) 33 } 34 35 // MatchAttr matches attributes to migrate to the x-agent-details subresource 36 func MatchAttr(attr ...string) { 37 oldAttrs = append(oldAttrs, attr...) 38 } 39 40 // RemoveTagPattern matches tags by a pattern for removal from the resource 41 func RemoveTagPattern(tags ...string) { 42 tagRegexes = append(tagRegexes, tags...) 43 } 44 45 type client interface { 46 ExecuteAPI(method, url string, queryParam map[string]string, buffer []byte) ([]byte, error) 47 GetAPIV1ResourceInstances(query map[string]string, URL string) ([]*apiv1.ResourceInstance, error) 48 UpdateResourceInstance(ri apiv1.Interface) (*apiv1.ResourceInstance, error) 49 CreateOrUpdateResource(data apiv1.Interface) (*apiv1.ResourceInstance, error) 50 CreateSubResource(rm apiv1.ResourceMeta, subs map[string]interface{}) error 51 DeleteResourceInstance(ri apiv1.Interface) error 52 GetResource(url string) (*apiv1.ResourceInstance, error) 53 } 54 55 type item struct { 56 ri *apiv1.ResourceInstance 57 update bool 58 } 59 60 type migrateFunc func(ri *apiv1.ResourceInstance) error 61 62 // AttributeMigration - used for migrating attributes to subresource 63 type AttributeMigration struct { 64 migration 65 riMutex sync.Mutex 66 } 67 68 // NewAttributeMigration creates a new AttributeMigration 69 func NewAttributeMigration(client client, cfg config.CentralConfig) *AttributeMigration { 70 return &AttributeMigration{ 71 migration: migration{ 72 client: client, 73 cfg: cfg, 74 }, 75 riMutex: sync.Mutex{}, 76 } 77 } 78 79 // Migrate - receives an APIService as a ResourceInstance, and checks if an attribute migration should be performed. 80 // If a migration should occur, then the APIService, Instances, and Revisions, that refer to the APIService will all have their attributes updated. 81 func (m *AttributeMigration) Migrate(_ context.Context, ri *apiv1.ResourceInstance) (*apiv1.ResourceInstance, error) { 82 if ri.Kind != management.APIServiceGVK().Kind { 83 return ri, nil 84 } 85 86 // skip migration if x-agent-details is found for the service. 87 details := util.GetAgentDetails(ri) 88 if len(details) > 0 { 89 return ri, nil 90 } 91 92 log.Debugf("migrating attributes for service: %s", ri.Name) 93 94 funcs := []migrateFunc{ 95 m.updateSvc, 96 m.updateInst, 97 } 98 99 errCh := make(chan error, len(funcs)) 100 wg := &sync.WaitGroup{} 101 102 for _, f := range funcs { 103 wg.Add(1) 104 105 go func(fun migrateFunc) { 106 defer wg.Done() 107 108 err := fun(ri) 109 errCh <- err 110 }(f) 111 } 112 113 wg.Wait() 114 close(errCh) 115 116 for e := range errCh { 117 if e != nil { 118 return ri, e 119 } 120 } 121 122 log.Debugf("finished migrating attributes for service: %s", ri.Name) 123 124 return ri, nil 125 } 126 127 // updateSvc updates the attributes on service in place, then updates on api server. 128 func (m *AttributeMigration) updateSvc(ri *apiv1.ResourceInstance) error { 129 url := fmt.Sprintf("%s/%s", m.cfg.GetServicesURL(), ri.Name) 130 r, err := m.getRI(url) 131 if err != nil { 132 return err 133 } 134 item := updateAttrs(r) 135 if !item.update { 136 return nil 137 } 138 139 // replace the address value so that the Migrate func can return the updated resource instance 140 m.riMutex.Lock() 141 defer m.riMutex.Unlock() 142 *ri = *item.ri 143 144 return m.updateRI(item.ri) 145 } 146 147 // updateInst gets a list of instances for the service and updates their attributes. 148 func (m *AttributeMigration) updateInst(ri *apiv1.ResourceInstance) error { 149 m.riMutex.Lock() 150 defer m.riMutex.Unlock() 151 152 q := map[string]string{ 153 "query": queryFuncByMetadataID(ri.Metadata.ID), 154 } 155 url := m.cfg.GetInstancesURL() 156 if err := m.migrate(url, q); err != nil { 157 return err 158 } 159 160 return nil 161 } 162 163 func (m *AttributeMigration) migrate(resourceURL string, query map[string]string) error { 164 resources, err := m.client.GetAPIV1ResourceInstances(query, resourceURL) 165 if err != nil { 166 return err 167 } 168 169 items := make([]item, 0) 170 171 for _, ri := range resources { 172 item := updateAttrs(ri) 173 items = append(items, item) 174 } 175 176 wg := &sync.WaitGroup{} 177 errCh := make(chan error, len(items)) 178 179 for _, item := range items { 180 if !item.update { 181 continue 182 } 183 184 wg.Add(1) 185 186 go func(ri *apiv1.ResourceInstance) { 187 defer wg.Done() 188 189 err := m.updateRI(ri) 190 errCh <- err 191 }(item.ri) 192 } 193 194 wg.Wait() 195 close(errCh) 196 197 for e := range errCh { 198 if e != nil { 199 return e 200 } 201 } 202 203 return nil 204 } 205 206 func updateAttrs(ri *apiv1.ResourceInstance) item { 207 details := util.GetAgentDetails(ri) 208 if details == nil { 209 details = make(map[string]interface{}) 210 } 211 212 item := item{ 213 ri: ri, 214 update: false, 215 } 216 217 for _, attr := range oldAttrs { 218 if _, ok := ri.Attributes[attr]; ok { 219 details[attr] = ri.Attributes[attr] 220 delete(ri.Attributes, attr) 221 item.update = true 222 } 223 } 224 225 for _, reg := range regexes { 226 for attr := range ri.Attributes { 227 if ok, _ := regexp.MatchString(reg, attr); ok { 228 details[attr] = ri.Attributes[attr] 229 delete(ri.Attributes, attr) 230 item.update = true 231 } 232 } 233 } 234 235 var tags []string 236 237 for _, tag := range ri.Tags { 238 for _, reg := range tagRegexes { 239 if ok, _ := regexp.MatchString(reg, tag); ok { 240 item.update = true 241 } else { 242 tags = append(tags, tag) 243 } 244 } 245 } 246 247 ri.Tags = tags 248 249 util.SetAgentDetails(ri, details) 250 251 return item 252 }