github.com/polarismesh/polaris@v1.17.8/admin/job/job.go (about) 1 /** 2 * Tencent is pleased to support the open source community by making Polaris available. 3 * 4 * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. 5 * 6 * Licensed under the BSD 3-Clause License (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * https://opensource.org/licenses/BSD-3-Clause 11 * 12 * Unless required by applicable law or agreed to in writing, software distributed 13 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 14 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 15 * specific language governing permissions and limitations under the License. 16 */ 17 18 package job 19 20 import ( 21 "context" 22 "fmt" 23 "os" 24 "time" 25 26 "github.com/polarismesh/polaris/cache" 27 commonlog "github.com/polarismesh/polaris/common/log" 28 "github.com/polarismesh/polaris/common/utils" 29 "github.com/polarismesh/polaris/service" 30 "github.com/polarismesh/polaris/store" 31 ) 32 33 var log = commonlog.GetScopeOrDefaultByName(commonlog.DefaultLoggerName) 34 35 // MaintainJobs 36 type MaintainJobs struct { 37 jobs map[string]maintainJob 38 startedJobs map[string]maintainJob 39 storage store.Store 40 cancel context.CancelFunc 41 } 42 43 // NewMaintainJobs 44 func NewMaintainJobs(namingServer service.DiscoverServer, cacheMgn *cache.CacheManager, 45 storage store.Store) *MaintainJobs { 46 return &MaintainJobs{ 47 jobs: map[string]maintainJob{ 48 "DeleteUnHealthyInstance": &deleteUnHealthyInstanceJob{ 49 namingServer: namingServer, storage: storage}, 50 "DeleteEmptyService": &deleteEmptyServiceJob{ 51 namingServer: namingServer, cacheMgn: cacheMgn, storage: storage}, 52 "CleanDeletedInstances": &cleanDeletedInstancesJob{ 53 storage: storage}, 54 "CleanDeletedClients": &cleanDeletedClientsJob{ 55 storage: storage}, 56 "CleanConfigReleaseHistory": &cleanConfigFileHistoryJob{ 57 storage: storage}, 58 }, 59 startedJobs: map[string]maintainJob{}, 60 storage: storage, 61 } 62 } 63 64 // StartMaintainJobs 65 func (mj *MaintainJobs) StartMaintianJobs(configs []JobConfig) error { 66 ctx, cancel := context.WithCancel(context.Background()) 67 mj.cancel = cancel 68 for _, cfg := range configs { 69 if !cfg.Enable { 70 log.Infof("[Maintain][Job] job (%s) not enable", cfg.Name) 71 continue 72 } 73 jobName := parseJobName(cfg.Name) 74 job, ok := mj.findAdminJob(jobName) 75 if !ok { 76 return fmt.Errorf("[Maintain][Job] job (%s) not exist", jobName) 77 } 78 if _, ok := mj.startedJobs[jobName]; ok { 79 return fmt.Errorf("[Maintain][Job] job (%s) duplicated", jobName) 80 } 81 if err := job.init(cfg.Option); err != nil { 82 log.Errorf("[Maintain][Job] job (%s) fail to init, err: %v", jobName, err) 83 return fmt.Errorf("[Maintain][Job] job (%s) fail to init", jobName) 84 } 85 if err := mj.storage.StartLeaderElection(store.ElectionKeyMaintainJobPrefix + jobName); err != nil { 86 log.Errorf("[Maintain][Job][%s] start leader election err: %v", jobName, err) 87 return err 88 } 89 runAdminJob(ctx, jobName, job.interval(), job, mj.storage) 90 mj.startedJobs[jobName] = job 91 } 92 return nil 93 } 94 95 func parseJobName(name string) string { 96 // 兼容老配置 97 if name == "DeleteEmptyAutoCreatedService" { 98 name = "DeleteEmptyService" 99 } 100 return name 101 } 102 103 func (mj *MaintainJobs) findAdminJob(name string) (maintainJob, bool) { 104 job, ok := mj.jobs[name] 105 if !ok { 106 return nil, false 107 } 108 109 return job, true 110 } 111 112 // StopMaintainJobs 113 func (mj *MaintainJobs) StopMaintainJobs() { 114 if mj.cancel != nil { 115 mj.cancel() 116 } 117 mj.startedJobs = map[string]maintainJob{} 118 } 119 120 func runAdminJob(ctx context.Context, name string, interval time.Duration, job maintainJob, storage store.Store) { 121 f := func() { 122 if !storage.IsLeader(store.ElectionKeyMaintainJobPrefix + name) { 123 log.Infof("[Maintain][Job][%s] I am follower", name) 124 job.clear() 125 return 126 } 127 log.Infof("[Maintain][Job][%s] I am leader, job start", name) 128 job.execute() 129 log.Infof("[Maintain][Job][%s] I am leader, job end", name) 130 } 131 132 ticker := time.NewTicker(interval) 133 go func(ctx context.Context) { 134 for { 135 select { 136 case <-ctx.Done(): 137 return 138 case <-ticker.C: 139 f() 140 } 141 } 142 }(ctx) 143 } 144 145 type maintainJob interface { 146 init(cfg map[string]interface{}) error 147 execute() 148 clear() 149 interval() time.Duration 150 } 151 152 func getMasterAccountToken(storage store.Store) (string, error) { 153 mainUser := os.Getenv("POLARIS_MAIN_USER") 154 if mainUser == "" { 155 mainUser = "polaris" 156 } 157 user, err := storage.GetUserByName(mainUser, "") 158 if err != nil { 159 return "", err 160 } 161 if user == nil { 162 return "", fmt.Errorf("polaris main user: %s not found", mainUser) 163 } 164 return user.Token, nil 165 } 166 167 func buildContext(storage store.Store) (context.Context, error) { 168 token, err := getMasterAccountToken(storage) 169 if err != nil { 170 return nil, err 171 } 172 ctx := context.Background() 173 ctx = context.WithValue(ctx, utils.ContextAuthTokenKey, token) 174 ctx = context.WithValue(ctx, utils.ContextOperator, "maintain-job") 175 return ctx, nil 176 }