github.com/Axway/agent-sdk@v1.1.101/pkg/agent/resource/manager.go (about) 1 package resource 2 3 import ( 4 "fmt" 5 "os" 6 "strconv" 7 "strings" 8 "time" 9 10 "github.com/Axway/agent-sdk/pkg/apic" 11 apiv1 "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/api/v1" 12 v1 "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/api/v1" 13 management "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/management/v1alpha1" 14 "github.com/Axway/agent-sdk/pkg/apic/definitions" 15 "github.com/Axway/agent-sdk/pkg/config" 16 "github.com/Axway/agent-sdk/pkg/util" 17 "github.com/Axway/agent-sdk/pkg/util/errors" 18 "github.com/Axway/agent-sdk/pkg/util/log" 19 ) 20 21 // QA EnvVars 22 const qaTriggerSevenDayRefreshCache = "QA_CENTRAL_TRIGGER_REFRESH_CACHE" 23 24 type EventSyncCache interface { 25 RebuildCache() 26 } 27 28 // Manager - interface to manage agent resource 29 type Manager interface { 30 OnConfigChange(cfg config.CentralConfig, apicClient apic.Client) 31 GetAgentResource() *apiv1.ResourceInstance 32 SetAgentResource(agentResource *apiv1.ResourceInstance) 33 FetchAgentResource() error 34 UpdateAgentStatus(status, prevStatus, message string) error 35 AddUpdateAgentDetails(key, value string) 36 SetRebuildCacheFunc(rebuildCache EventSyncCache) 37 } 38 39 type executeAPIClient interface { 40 CreateSubResource(rm v1.ResourceMeta, subs map[string]interface{}) error 41 GetResource(url string) (*v1.ResourceInstance, error) 42 CreateResourceInstance(ri apiv1.Interface) (*apiv1.ResourceInstance, error) 43 UpdateResourceInstance(ri apiv1.Interface) (*apiv1.ResourceInstance, error) 44 } 45 46 type agentResourceManager struct { 47 agentResource *apiv1.ResourceInstance 48 prevAgentResHash uint64 49 apicClient executeAPIClient 50 cfg config.CentralConfig 51 agentResourceChangeHandler func() 52 agentDetails map[string]interface{} 53 logger log.FieldLogger 54 rebuildCache EventSyncCache 55 } 56 57 // NewAgentResourceManager - Create a new agent resource manager 58 func NewAgentResourceManager(cfg config.CentralConfig, apicClient executeAPIClient, agentResourceChangeHandler func()) (Manager, error) { 59 60 logger := log.NewFieldLogger(). 61 WithPackage("sdk.agent"). 62 WithComponent("agentResourceManager") 63 m := &agentResourceManager{ 64 cfg: cfg, 65 apicClient: apicClient, 66 agentResourceChangeHandler: agentResourceChangeHandler, 67 agentDetails: make(map[string]interface{}), 68 logger: logger, 69 } 70 71 if m.getAgentResourceType() != nil { 72 err := m.FetchAgentResource() 73 if err != nil { 74 return nil, err 75 } 76 } else if m.cfg.GetAgentName() != "" { 77 return nil, errors.Wrap(apic.ErrCentralConfig, "Agent name cannot be set. Config is used only for agents with API server resource definition") 78 } 79 return m, nil 80 } 81 82 // OnConfigChange - Applies central config change to the manager 83 func (a *agentResourceManager) OnConfigChange(cfg config.CentralConfig, apicClient apic.Client) { 84 a.apicClient = apicClient 85 a.cfg = cfg 86 } 87 88 // GetAgentResource - Returns the agent resource 89 func (a *agentResourceManager) GetAgentResource() *apiv1.ResourceInstance { 90 return a.agentResource 91 } 92 93 // SetAgentResource - Sets the agent resource which triggers agent resource change handler 94 func (a *agentResourceManager) SetAgentResource(agentResource *apiv1.ResourceInstance) { 95 if agentResource != nil && agentResource.Name == a.cfg.GetAgentName() { 96 a.agentResource = agentResource 97 a.onResourceChange() 98 } 99 } 100 101 func (a *agentResourceManager) SetRebuildCacheFunc(rebuildCache EventSyncCache) { 102 a.rebuildCache = rebuildCache 103 } 104 105 // FetchAgentResource - Gets the agent resource using API call to apiserver 106 func (a *agentResourceManager) FetchAgentResource() error { 107 if a.cfg.GetAgentName() == "" { 108 return nil 109 } 110 111 var err error 112 a.agentResource, err = a.getAgentResource() 113 if err != nil { 114 if strings.Contains(err.Error(), "404") { 115 a.agentResource, err = a.createAgentResource() 116 if err != nil { 117 return err 118 } 119 } else { 120 return err 121 } 122 } else { 123 a.checkAgentResource() 124 } 125 126 a.onResourceChange() 127 return nil 128 } 129 130 // UpdateAgentStatus - Updates the agent status in agent resource 131 func (a *agentResourceManager) UpdateAgentStatus(status, prevStatus, message string) error { 132 if a.cfg == nil || a.cfg.GetAgentName() == "" { 133 return nil 134 } 135 136 if a.agentResource == nil { 137 return nil 138 } 139 140 agentInstance := a.getAgentResourceType() 141 142 statusSubResourceName := management.DiscoveryAgentStatusSubResourceName 143 // using discovery agent status here, but all agent status resources have the same structure 144 agentInstance.SubResources[statusSubResourceName] = management.DiscoveryAgentStatus{ 145 Version: config.AgentVersion, 146 LatestAvailableVersion: config.AgentLatestVersion, 147 State: status, 148 PreviousState: prevStatus, 149 Message: message, 150 LastActivityTime: getTimestamp(), 151 SdkVersion: config.SDKVersion, 152 } 153 154 // See if we need to rebuildCache 155 timeToRebuild, _ := a.shouldRebuildCache() 156 if timeToRebuild && a.rebuildCache != nil { 157 a.rebuildCache.RebuildCache() 158 } 159 160 subResources := make(map[string]interface{}) 161 subResources[statusSubResourceName] = agentInstance.SubResources[statusSubResourceName] 162 // add any details 163 if len(a.agentDetails) > 0 { 164 util.SetAgentDetails(agentInstance, a.agentDetails) 165 subResources[definitions.XAgentDetails] = agentInstance.SubResources[definitions.XAgentDetails] 166 } 167 168 err := a.apicClient.CreateSubResource(agentInstance.ResourceMeta, subResources) 169 return err 170 } 171 172 // 1. On UpdateAgentStatus, if x-agent-details, key "cacheUpdateTime" doesn't exist or empty, rebuild cache to populate cacheUpdateTime 173 // 2. On UpdateAgentStatus, if x-agent-details exists, check to see if its past 7 days since rebuildCache was ran. If its pass 7 days, rebuildCache 174 func (a *agentResourceManager) shouldRebuildCache() (bool, error) { 175 rebuildCache := false 176 agentInstance := a.GetAgentResource() 177 agentDetails := agentInstance.GetSubResource(definitions.XAgentDetails) 178 179 if agentDetails == nil { 180 // x-agent-details hasn't been established yet. Rebuild cache to populate cacheUpdateTime 181 a.logger.Trace("create x-agent-detail subresource and add key 'cacheUpdateTime'") 182 rebuildCache = true 183 } else { 184 value, exists := agentDetails.(map[string]interface{})["cacheUpdateTime"] 185 if value != nil { 186 // get current cacheUpdateTime from x-agent-details 187 convToTimestamp, err := strconv.ParseInt(value.(string), 10, 64) 188 if err != nil { 189 return false, err 190 } 191 currentCacheUpdateTime := time.Unix(0, convToTimestamp) 192 a.logger.Tracef("the current scheduled refresh cache date - %s", time.Unix(0, currentCacheUpdateTime.UnixNano()).Format("2006-01-02 15:04:05.000000")) 193 194 // check to see if 7 days have passed since last refresh cache. currentCacheUpdateTime is the date at the time we rebuilt cache plus 7 days(in event sync - RebuildCache) 195 if a.getCurrentTime() > currentCacheUpdateTime.UnixNano() { 196 a.logger.Trace("the current date is greater than the current scheduled refresh date - time to rebuild cache") 197 rebuildCache = true 198 } 199 } else { 200 if !exists { 201 // x-agent-details exists, however, cacheUpdateTime key doesn't exist. Rebuild cache to populate cacheUpdateTime 202 a.logger.Trace("update x-agent-detail subresource and add key 'cacheUpdateTime'") 203 rebuildCache = true 204 } 205 } 206 } 207 208 return rebuildCache, nil 209 } 210 211 func (a *agentResourceManager) getCurrentTime() int64 { 212 val := os.Getenv(qaTriggerSevenDayRefreshCache) 213 if val == "" { 214 // if this isn't set, then just pass back the current time 215 return time.Now().UnixNano() 216 } 217 // if this is set, then pass back the current time, plus 7 days to trigger a rebuild 218 return time.Now().Add(7 * 24 * time.Hour).UnixNano() 219 } 220 221 // GetAgentDetails - Gets current agent details 222 func (a *agentResourceManager) GetAgentDetails() map[string]interface{} { 223 return a.agentDetails 224 } 225 226 // AddUpdateAgentDetails - Adds a new or Updates an existing key on the agent details sub resource 227 func (a *agentResourceManager) AddUpdateAgentDetails(key, value string) { 228 a.agentDetails[key] = value 229 } 230 231 // getTimestamp - Returns current timestamp formatted for API Server 232 func getTimestamp() apiv1.Time { 233 activityTime := time.Now() 234 newV1Time := apiv1.Time(activityTime) 235 return newV1Time 236 } 237 238 func applyResConfigToCentralConfig(cfg *config.CentralConfiguration, resCfgAdditionalTags, resCfgTeamID, resCfgLogLevel string) { 239 agentResLogLevel := log.GlobalLoggerConfig.GetLevel() 240 if resCfgLogLevel != "" && !strings.EqualFold(agentResLogLevel, resCfgLogLevel) { 241 log.GlobalLoggerConfig.Level(resCfgLogLevel).Apply() 242 } 243 244 if resCfgAdditionalTags != "" && !strings.EqualFold(cfg.TagsToPublish, resCfgAdditionalTags) { 245 cfg.TagsToPublish = resCfgAdditionalTags 246 } 247 248 // If config team is blank, check resource team name. If resource team name is not blank, use resource team name 249 if resCfgTeamID != "" && cfg.TeamName == "" { 250 cfg.SetTeamID(resCfgTeamID) 251 } 252 } 253 254 func (a *agentResourceManager) onResourceChange() { 255 if a.agentResource != nil { 256 subRes := a.agentResource.GetSubResource(definitions.XAgentDetails) 257 if details, ok := subRes.(map[string]interface{}); ok { 258 a.agentDetails = details 259 } 260 } 261 262 isChanged := (a.prevAgentResHash != 0) 263 agentResHash, _ := util.ComputeHash(a.agentResource) 264 if a.prevAgentResHash != 0 && a.prevAgentResHash == agentResHash { 265 isChanged = false 266 } 267 a.prevAgentResHash = agentResHash 268 if isChanged { 269 // merge agent resource config with central config 270 a.mergeResourceWithConfig() 271 if a.agentResourceChangeHandler != nil { 272 a.agentResourceChangeHandler() 273 } 274 } 275 } 276 277 func (a *agentResourceManager) getAgentResourceType() *v1.ResourceInstance { 278 var agentRes v1.Interface 279 switch a.cfg.GetAgentType() { 280 case config.DiscoveryAgent: 281 res := management.NewDiscoveryAgent(a.cfg.GetAgentName(), a.cfg.GetEnvironmentName()) 282 res.Spec.DataplaneType = config.AgentDataPlaneType 283 agentRes = res 284 case config.TraceabilityAgent: 285 res := management.NewTraceabilityAgent(a.cfg.GetAgentName(), a.cfg.GetEnvironmentName()) 286 res.Spec.DataplaneType = config.AgentDataPlaneType 287 agentRes = res 288 } 289 var agentInstance *v1.ResourceInstance 290 if agentRes != nil { 291 agentInstance, _ = agentRes.AsInstance() 292 } 293 return agentInstance 294 } 295 296 // GetAgentResource - returns the agent resource 297 func (a *agentResourceManager) createAgentResource() (*v1.ResourceInstance, error) { 298 agentRes := a.getAgentResourceType() 299 if agentRes == nil { 300 return nil, fmt.Errorf("unknown agent type") 301 } 302 a.logger. 303 WithField("scope", agentRes.Metadata.Scope). 304 WithField("kind", agentRes.Kind). 305 WithField("name", agentRes.Name). 306 Info("creating agent resource") 307 return a.apicClient.CreateResourceInstance(agentRes) 308 } 309 310 // GetAgentResource - returns the agent resource 311 func (a *agentResourceManager) checkAgentResource() (*v1.ResourceInstance, error) { 312 var agentRes v1.Interface 313 logger := a.logger.WithField("scope", a.agentResource.Metadata.Scope).WithField("kind", a.agentResource.Kind).WithField("name", a.agentResource.Name) 314 315 currDataplaneType := apic.Unidentified.String() 316 if a.agentResource.Kind == management.DiscoveryAgentGVK().Kind { 317 da := management.NewDiscoveryAgent("", "") 318 da.FromInstance(a.agentResource) 319 currDataplaneType = da.Spec.DataplaneType 320 da.Spec.DataplaneType = config.AgentDataPlaneType 321 agentRes = da 322 } else if a.agentResource.Kind == management.TraceabilityAgentGVK().Kind { 323 ta := management.NewTraceabilityAgent("", "") 324 ta.FromInstance(a.agentResource) 325 currDataplaneType = ta.Spec.DataplaneType 326 ta.Spec.DataplaneType = config.AgentDataPlaneType 327 agentRes = ta 328 } 329 330 // nothing to update 331 if currDataplaneType == config.AgentDataPlaneType { 332 return a.agentResource, nil 333 } 334 335 logger.Info("updating agent resource") 336 return a.apicClient.UpdateResourceInstance(agentRes) 337 } 338 339 // GetAgentResource - returns the agent resource 340 func (a *agentResourceManager) getAgentResource() (*v1.ResourceInstance, error) { 341 agentRes := a.getAgentResourceType() 342 if agentRes == nil { 343 return nil, fmt.Errorf("unknown agent type") 344 } 345 346 return a.apicClient.GetResource(agentRes.GetSelfLink()) 347 } 348 349 func (a *agentResourceManager) mergeResourceWithConfig() { 350 // IMP - To be removed once the model is in production 351 if a.cfg.GetAgentName() == "" { 352 return 353 } 354 355 switch a.getAgentResourceType().Kind { 356 case management.DiscoveryAgentGVK().Kind: 357 mergeDiscoveryAgentWithConfig(a.GetAgentResource(), a.cfg.(*config.CentralConfiguration)) 358 case management.TraceabilityAgentGVK().Kind: 359 mergeTraceabilityAgentWithConfig(a.GetAgentResource(), a.cfg.(*config.CentralConfiguration)) 360 default: 361 panic(ErrUnsupportedAgentType) 362 } 363 }