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  }