# Cli implementation ## Step1 : insexec/cli: - create package `insexec/cli/{cliname}` - {cliname} - commandbuilder // Define all commands - delegates to functions which enque the tasks - GetRootCommand // use commandbuilder to generate and expose commands - root-init.go - call `cliname.GetRootCommand` and attach to root command ### Example : CommandBuilder : ```go= type commandBuilder struct{} func newCommandBuilder() commandBuilder { return commandBuilder{} } func (it commandBuilder) setIpCommand() *cobra.Command { var ipSlice []string var adapterValue string setIpCmd := &cobra.Command{ Use: "set-ip", Short: "for setting ips", RunE: func(cmd *cobra.Command, args []string) error { return SetIp(ipSlice, &adapterValue) // delegating to a function }, } setIpCmd.Flags().StringSliceVarP(&ipSlice, ipsFlag, "", ipSlice, "ips") setIpCmd.Flags().StringVarP(&adapterValue, adapterFlag, "", "", "select adapter") return setIpCmd } func (it commandBuilder) buildRootCommand() *cobra.Command { ethernetCmd := &cobra.Command{ Use: "eth", Short: "manipulation of ethernet interfaces", } ethernetCmd.AddCommand(it.setIpCommand()) return ethernetCmd } // SetIp.go // enques the task of setting // we define model in "gitlab.com/evatix-go/insexec/taskreq/models" func SetIp(ipSlice []string, adapterValue *string) error { var taskPayload models.SetIpPayload if adapterValue != nil && len(*adapterValue) > 0 { taskPayload = models.SetIpPayload{ BaseAdapter: models.BaseAdapter{ Name: *adapterValue, }, Ips: ipSlice, } } else { result := adapter.GetDefaultAdapter() if result.ErrorWrapper.HasError() { return result.ErrorWrapper.CompiledErrorWithStackTraces() } taskPayload = models.SetIpPayload{ BaseAdapter: models.BaseAdapter{ Name: result.Value, }, Ips: ipSlice, } } return clicore.EnqueueTaskAndListen( taskcategory.Ethernet.Name(), ethernetenums.SetIp, // const defined in "gitlab.com/evatix-go/insexec/enums/subcommands/ethernetenums" &taskPayload, ) } ``` GetRootCommand : ```go= func GetRootCommand() *cobra.Command { return newCommandBuilder(). buildRootCommand() } // cli/root-init.go rootCmd.AddCommand(eth.GetRootCommand()) ``` ## Step2 : insexec/businessaction: - Create CliName.go (for example Ethernet.go) - implement HandlerMapper - Write logics to unmarshall the enqued data and provide handlerFuncMap (map[string]HandlerFunc) , HandlerFunc(func(payload []byte) *errorwrapper.Wrapper) Example : ```go= package businessaction import ( "fmt" "gitlab.com/evatix-go/errorwrapper" "gitlab.com/evatix-go/insfmt/ethernetinsfmt" "gitlab.com/evatix-go/insexec/enums/subcommands/ethernetenums" "gitlab.com/evatix-go/insexec/taskreq/models" ) type Ethernet struct { BaseBusinessAction } func NewEthernet( baseBusinessAction BaseBusinessAction, ) HandlerMapper { return &Ethernet{ BaseBusinessAction: baseBusinessAction, } } func (it *Ethernet) createHandlersMap() HandlerFuncMap { return HandlerFuncMap{ ethernetenums.SetIp: it.SetIpFunc, .... } } func (it *Ethernet) HandlersMap() HandlerFuncMap { if it.HandlerFuncMap != nil { return it.HandlerFuncMap } it.HandlerFuncMap = it.createHandlersMap() return it.HandlerFuncMap } func (it *Ethernet) setIp( adapterName string, ips []string, ) *errorwrapper.Wrapper { processingStr := fmt.Sprintf( "Attaching ips : %v, to interface : %v", ips, adapterName, ) it.logger.LogInfo(processingStr) return processSetIp(adapterName, ips) } func (it *Ethernet) SetIpFunc(payload []byte) *errorwrapper.Wrapper { var payloadModel models.SetIpPayload errW := DeserializeTo(payload, &payloadModel) if errW.HasError() { return errW } return it.setIp( payloadModel.Name, payloadModel.Ips) } // processSetIp.go func processSetIp(adapterName string, ipSlice []string) *errorwrapper.Wrapper { ethernet, ethernetErrW := adapter.GetSingleAdapter(adapterName) if ethernetErrW.HasError() { return ethernetErrW } directConfigs := []ethernetinsfmt.DirectEthernetApplyConfig{ { ApplyingAdapterName: ethernet.Name, BaseEthernetConfig: ethernetinsfmt.BaseEthernetConfig{ IP: ipSlice, Subnet: ethernet.IpCollection.GetSubnetMask4(), DefaultGateway: ethernet.DefaultGateway, DomainNameServers: &ethernetinsfmt.DomainNameServers{ Addresses: []string{ defaultGoogleDns, }, }, }, }, } // todo : alim, https://gitlab.com/evatix-go/insexec/-/issues/51 errCollection := errwrappers.Empty() ethernetapi.NewDirectApplyApi( directConfigs, errCollection, ).Apply() return errCollection.GetAsErrorWrapperPtr() } ``` ## Step3 : taskrunner/handlers: - create cliname.go (for example ethernet.go) - write logics as following ```go= package handlers import ( "context" "github.com/hibiken/asynq" "gitlab.com/evatix-go/insexec/businessaction" "gitlab.com/evatix-go/taskrunner/tasklogger" ) type ethernet struct { BaseHandler } func (it *ethernet) InjectLogger( wrappedLogger *tasklogger.WrappedLogger, ) asynq.HandlerFunc { it.WrappedLogger = wrappedLogger return it.Handler } func (it ethernet) Handler( ctx context.Context, task *asynq.Task, ) error { executor := BaseHandlerExecutor{ BaseHandler: it.BaseHandler, Ctx: ctx, Task: task, BusinessActionCreateFunc: businessaction.NewEthernet, // call the constructor of businessaction ServiceName: ethernetServiceName, // const } return executor.Execute() } func (it *ethernet) AsDefinitionWrapper() DefinitionWrapper { return it } ``` - vars.go ```go= import ( "sync" ) var ( Ethernet = &ethernet{} // expose, gets called by handlersmap package ) ``` ## Step4 : taskrunner/handlersmap: - add the handlers.Ethernet to its vars.go, with its taskcategory ```go= package handlersmap import ( "gitlab.com/evatix-go/insexec/enums/taskcategory" "gitlab.com/evatix-go/taskrunner/handlers" ) var ( RawHandlersMap = map[taskcategory.Variant]handlers.DefinitionWrapperContractsBinder{ taskcategory.Ethernet: handlers.Ethernet, } ) ``` ## Summary - Add commands and enque logic to `insexec/cli/{cliname}` - Add the generated commands to rootcmd (root-init.go) - Write business logic for the commands to `insexec/businessaction/CliName.go` - Add logic to taskrunner/handlers - Add handler to `taskrunner/handlersmap`