---
# System prepended metadata

title: Cli implementation

---

# 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`