github.com/kubewharf/katalyst-core@v0.5.3/cmd/katalyst-agent/app/agent/context.go (about) 1 /* 2 Copyright 2022 The Katalyst Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package agent 18 19 import ( 20 "context" 21 "fmt" 22 "os" 23 "time" 24 25 "go.uber.org/atomic" 26 "k8s.io/apimachinery/pkg/util/sets" 27 "k8s.io/client-go/tools/record" 28 "k8s.io/klog/v2" 29 "k8s.io/kubernetes/pkg/kubelet/config" 30 "k8s.io/kubernetes/pkg/kubelet/pluginmanager" 31 "k8s.io/kubernetes/pkg/kubelet/pluginmanager/cache" 32 33 "github.com/kubewharf/katalyst-api/pkg/plugins/skeleton" 34 katalystbase "github.com/kubewharf/katalyst-core/cmd/base" 35 katalystconfig "github.com/kubewharf/katalyst-core/pkg/config" 36 "github.com/kubewharf/katalyst-core/pkg/metaserver" 37 ) 38 39 // InitFunc is used to construct the framework of agent component; all components 40 // should be initialized before any component starts to run, to make sure the 41 // dependencies are well handled before the running logic starts. 42 // the returned parameters are consisted of several parts 43 // - bool indicates whether the agent component needs to be started by calling Run function 44 // - Component indicates the corresponding component if starting is needed 45 // - error indicates whether the agent is succeeded to be initialized 46 type InitFunc func(agentCtx *GenericContext, conf *katalystconfig.Configuration, 47 extraConf interface{}, agentName string) (bool, Component, error) 48 49 // Component is used as a common abstraction for all agent components. 50 type Component interface { 51 // Run performs those running logic for each agent component 52 // Run works in a blocking way, and all Component should wait 53 // until the given ctx is done 54 Run(ctx context.Context) 55 } 56 57 // ComponentStub is used as testing implementation for agent Component. 58 type ComponentStub struct{} 59 60 func (c ComponentStub) Run(_ context.Context) {} 61 62 var _ Component = ComponentStub{} 63 64 type PluginWrapper struct { 65 skeleton.GenericPlugin 66 } 67 68 func (p *PluginWrapper) Run(ctx context.Context) { 69 if err := p.Start(); err != nil { 70 klog.Fatalf("start %v failed: %v", p.Name(), err) 71 } 72 73 klog.Infof("plugin wrapper %s started", p.Name()) 74 <-ctx.Done() 75 if err := p.Stop(); err != nil { 76 klog.Errorf("stop %v failed: %v", p.Name(), err) 77 } 78 } 79 80 // GenericContext is used by katalyst-agent, and it's constructed based on 81 // unified katalyst-genetic context. 82 type GenericContext struct { 83 *katalystbase.GenericContext 84 85 // those are shared among other agent components 86 *metaserver.MetaServer 87 pluginmanager.PluginManager 88 } 89 90 func NewGenericContext(base *katalystbase.GenericContext, conf *katalystconfig.Configuration) (*GenericContext, error) { 91 metaServer, err := metaserver.NewMetaServer(base.Client, base.EmitterPool.GetDefaultMetricsEmitter(), conf) 92 if err != nil { 93 return nil, fmt.Errorf("failed init meta server: %s", err) 94 } 95 96 pluginMgr, err := newPluginManager(conf) 97 if err != nil { 98 return nil, fmt.Errorf("failed init plugin manager: %s", err) 99 } 100 101 return &GenericContext{ 102 GenericContext: base, 103 MetaServer: metaServer, 104 PluginManager: pluginMgr, 105 }, nil 106 } 107 108 func (c *GenericContext) Run(ctx context.Context) { 109 go c.GenericContext.Run(ctx) 110 go c.PluginManager.Run(config.NewSourcesReady(func(_ sets.String) bool { return true }), ctx.Done()) 111 go c.MetaServer.Run(ctx) 112 <-ctx.Done() 113 } 114 115 // customizedPluginManager requires that handlers must be added before 116 // it really runs to avoid logging error logs too frequently 117 type customizedPluginManager struct { 118 enabled atomic.Bool 119 pluginmanager.PluginManager 120 } 121 122 // Run successes iff at-least-one handler has been registered 123 func (p *customizedPluginManager) Run(sourcesReady config.SourcesReady, stopCh <-chan struct{}) { 124 ticker := time.NewTicker(time.Second) 125 for ; true; <-ticker.C { 126 if p.enabled.Load() { 127 break 128 } 129 klog.V(5).Infof("skip starting plugin-manager without handlers added") 130 } 131 ticker.Stop() 132 133 klog.Infof("started plugin-manager since handlers have been enabled") 134 p.PluginManager.Run(sourcesReady, stopCh) 135 } 136 137 func (p *customizedPluginManager) AddHandler(pluginType string, pluginHandler cache.PluginHandler) { 138 p.enabled.Store(true) 139 p.PluginManager.AddHandler(pluginType, pluginHandler) 140 } 141 142 // newPluginManager initializes the registration logic for extendable plugins. 143 // all plugin manager added to generic context must use the same socket and 144 // default checkpoint path, and if some plugin needs to use a different socket path, 145 // it should create the plugin manager itself. 146 func newPluginManager(conf *katalystconfig.Configuration) (pluginmanager.PluginManager, error) { 147 // make sure plugin registration directory already exist 148 err := os.MkdirAll(conf.PluginRegistrationDir, os.FileMode(0o755)) 149 if err != nil { 150 return nil, fmt.Errorf("initializes plugin registration dir failed: %s", err) 151 } 152 153 return &customizedPluginManager{ 154 enabled: *atomic.NewBool(false), 155 PluginManager: pluginmanager.NewPluginManager( 156 conf.PluginRegistrationDir, /* sockDir */ 157 &record.FakeRecorder{}, 158 ), 159 }, nil 160 }