# 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: ðernetinsfmt.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 = ðernet{} // 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`