Repository: chef/automate
Pull request #7854 (2023-05-03 06:52:39)

Chef 2126 create cli subcommand to start verification as systemd service ok
### :nut_and_bolt: Description: What code changed, and why?

<!-- /!\ Please ensure that you are NOT disclosing any customer information without their consent /!\ -->

### :chains: Related Resources
https://chefio.atlassian.net/browse/CHEF-2126

### :+1: Definition of Done

### :athletic_shoe: How to Build and Test the Change

### :white_check_mark: Checklist

**All PRs** must tick these:

- [ ] I have read the [CONTRIBUTING document](https://github.com/chef/automate/blob/master/CONTRIBUTING.md).
- [ ] All commits signed-off for the [Developer Certification of Origin](https://github.com/chef/chef/blob/master/CONTRIBUTING.md#developer-certification-of-origin-dco).

With occasional exceptions, all PRs **from Progress employees** must tick these:

- [ ] Is the code clear? *(complicated code or lots of comments--subdivide and use well-named methods, meaningful variable names, etc.)*
- [ ] Consistency checked? *(user notifications, user prompts, visual patterns, code patterns, variable names)*
- [ ] Repeated code blocks eliminated? *(adapt and reuse existing components, blocks, functions, etc.)*
- [ ] Spelling, grammar, typos checked? *(at a minimum use `make spell` in any component directory)*
- [ ] Code well-formatted? *(indents, line breaks, etc. improve rather than hinder readability)*

All PRs **from Progress employees** should tick these if appropriate:

- [ ] Tests added/updated? (all new code needs new tests)
- [ ] Docs added/updated? (all customer-facing changes)

*Please add a note next to any checkbox above if you are NOT ticking it.*

### :camera: Screenshots, if applicable

https://progresssoftware.sharepoint.com/:v:/s/ChefCoreC/EbFG2ggblUVMs45bCOSYu8MB5AvRWO3B5JDZqc9OPK7t_g?e=7Yfhfs


netlify[bot]: ### <span aria-hidden="true">👷</span> Deploy Preview for *chef-automate* processing.


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | 361824dc3ab3c1d61bf8181e1f2d16e550753d85 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/chef-automate/deploys/645204b93eae6b0008970168 |
sonarqube-for-infrastructure-prod[bot]: **SonarQube Quality Gate**

[![Quality Gate failed](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/QualityGateBadge/failed-16px.png 'Quality Gate failed')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854)

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854) [![7.4%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '7.4%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854) 7.35% Coverage on New Code (is less than 80%)  

[See analysis details on SonarQube](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854)


sonarqube-for-infrastructure-prod[bot]: **SonarQube Quality Gate**

[![Quality Gate failed](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/QualityGateBadge/failed-16px.png 'Quality Gate failed')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854)

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854) [![7.4%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '7.4%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854) 7.35% Coverage on New Code (is less than 80%)  

[See analysis details on SonarQube](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854)


sonarqube-for-infrastructure-prod[bot]: **SonarQube Quality Gate**

[![Quality Gate failed](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/QualityGateBadge/failed-16px.png 'Quality Gate failed')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854)

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854) [![73.9%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/60-16px.png '73.9%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854) 73.91% Coverage on New Code (is less than 80%)  

[See analysis details on SonarQube](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854)


sonarqube-for-infrastructure-prod[bot]: **SonarQube Quality Gate**

[![Quality Gate failed](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/QualityGateBadge/failed-16px.png 'Quality Gate failed')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854)

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854) [![75.3%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/60-16px.png '75.3%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854) 75.32% Coverage on New Code (is less than 80%)  

[See analysis details on SonarQube](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854)


sonarqube-for-infrastructure-prod[bot]: **SonarQube Quality Gate**

[![Quality Gate failed](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/QualityGateBadge/failed-16px.png 'Quality Gate failed')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854)

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854) [![78.2%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/60-16px.png '78.2%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854) 78.21% Coverage on New Code (is less than 80%)  

[See analysis details on SonarQube](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854)


sonarqube-for-infrastructure-prod[bot]: **SonarQube Quality Gate**

[![Quality Gate failed](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/QualityGateBadge/failed-16px.png 'Quality Gate failed')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854)

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854) [![77.8%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/60-16px.png '77.8%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854) 77.78% Coverage on New Code (is less than 80%)  

[See analysis details on SonarQube](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854)


sonarqube-for-infrastructure-prod[bot]: **SonarQube Quality Gate**

[![Quality Gate failed](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/QualityGateBadge/failed-16px.png 'Quality Gate failed')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854)

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854) [![22.1%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '22.1%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854) 22.11% Coverage on New Code (is less than 80%)  

[See analysis details on SonarQube](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854)


sonarqube-for-infrastructure-prod[bot]: **SonarQube Quality Gate**

[![Quality Gate failed](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/QualityGateBadge/failed-16px.png 'Quality Gate failed')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854)

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854) [![21.6%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '21.6%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854) 21.65% Coverage on New Code (is less than 80%)  

[See analysis details on SonarQube](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854)


sonarqube-for-infrastructure-prod[bot]: **SonarQube Quality Gate**

[![Quality Gate failed](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/QualityGateBadge/failed-16px.png 'Quality Gate failed')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854)

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854) [![18.9%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '18.9%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854) 18.88% Coverage on New Code (is less than 80%)  

[See analysis details on SonarQube](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854)


sonarqube-for-infrastructure-prod[bot]: **SonarQube Quality Gate**

[![Quality Gate failed](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/QualityGateBadge/failed-16px.png 'Quality Gate failed')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854)

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854) [![18.9%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '18.9%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854) 18.88% Coverage on New Code (is less than 80%)  

[See analysis details on SonarQube](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854)


sonarqube-for-infrastructure-prod[bot]: **SonarQube Quality Gate**

[![Quality Gate failed](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/QualityGateBadge/failed-16px.png 'Quality Gate failed')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854)

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854) [![18.9%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '18.9%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854) 18.88% Coverage on New Code (is less than 80%)  

[See analysis details on SonarQube](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854)


sonarqube-for-infrastructure-prod[bot]: **SonarQube Quality Gate**

[![Quality Gate failed](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/QualityGateBadge/failed-16px.png 'Quality Gate failed')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854)

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854) [![18.9%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '18.9%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854) 18.88% Coverage on New Code (is less than 80%)  

[See analysis details on SonarQube](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7854)



Patch:
diff --git a/.bldr.toml b/.bldr.toml
index 6818e031d..5f3d1df6a 100644
--- a/.bldr.toml
+++ b/.bldr.toml
@@ -445,6 +445,7 @@ paths = [
   "lib/grpc/*",
   "lib/httputils/*",
   "lib/io/*",
+  "lib/logger/*",
   "lib/majorupgrade_utils/*",
   "lib/platform/command/*",
   "lib/platform/config/*",
diff --git a/VERSION b/VERSION
index 3062458da..d5353ba98 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-4.5.178
\ No newline at end of file
+4.5.180
\ No newline at end of file
diff --git a/components/automate-cli/cmd/chef-automate/automateClusterCtlUtil.go b/components/automate-cli/cmd/chef-automate/automateClusterCtlUtil.go
index 463143d87..8a0ed127a 100644
--- a/components/automate-cli/cmd/chef-automate/automateClusterCtlUtil.go
+++ b/components/automate-cli/cmd/chef-automate/automateClusterCtlUtil.go
@@ -515,20 +515,6 @@ func executeShellCommand(command string, args []string, workingDir string) error
 	return err
 }
 
-func executeShellCommandAndGet(command string, args []string, workingDir string) error {
-	writer.Printf("\n%s command execution started \n\n\n", command)
-	c := exec.Command(command, args...)
-	c.Stdin = os.Stdin
-	if len(workingDir) > 0 {
-		c.Dir = workingDir
-	}
-	c.Stdout = io.MultiWriter(os.Stdout)
-	c.Stderr = io.MultiWriter(os.Stderr)
-	err := c.Run()
-	writer.Printf("%s command execution done, exiting\n", command)
-	return err
-}
-
 func extarctVersionAndRelease(filename string) (string, string) {
 	r := regexp.MustCompile(RELEASE_AND_VERSION_PATTERN)
 	match := r.FindStringSubmatch(filename)
diff --git a/components/automate-cli/cmd/chef-automate/verify.go b/components/automate-cli/cmd/chef-automate/verify.go
index d3191e908..e8edb802c 100644
--- a/components/automate-cli/cmd/chef-automate/verify.go
+++ b/components/automate-cli/cmd/chef-automate/verify.go
@@ -8,11 +8,16 @@ import (
 	"github.com/chef/automate/components/automate-cli/pkg/docs"
 	"github.com/chef/automate/components/automate-cli/pkg/status"
 	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/server"
+	"github.com/chef/automate/components/automate-cli/pkg/verifysystemdcreate"
 	verification "github.com/chef/automate/lib/verification"
 	"github.com/spf13/cobra"
 )
 
-const VERIFY_SERVER_PORT = "VERIFY_SERVER_PORT"
+const (
+	VERIFY_SERVER_PORT        = "VERIFY_SERVER_PORT"
+	BINARY_DESTINATION_FOLDER = "/etc/automate-verify"
+	SYSTEMD_PATH              = "/etc/systemd/system"
+)
 
 type verifyCmdFlags struct {
 	file                      string
@@ -36,6 +41,8 @@ type verifyCmdFlow struct {
 
 type verifyServeCmdFlow struct{}
 
+type verifySystemdCreateFlow struct{}
+
 func init() {
 	flagsObj := verifyCmdFlags{}
 
@@ -62,6 +69,24 @@ func init() {
 		RunE: verifyServeCmdFunc(&flagsObj),
 	}
 
+	verifySystemdServiceCmd := &cobra.Command{
+		Use:   "systemd-service COMMAND",
+		Short: "Systemd utilities for verify command",
+		Annotations: map[string]string{
+			docs.Compatibility: docs.Compatibility,
+		},
+	}
+
+	verifySystemdServiceCreateCmd := &cobra.Command{
+		Use:   "create",
+		Short: "Start verify server as systemd service",
+		Annotations: map[string]string{
+			docs.Compatibility: docs.Compatibility,
+		},
+		Args: cobra.ExactArgs(0),
+		RunE: verifySystemdCreateFunc(&flagsObj),
+	}
+
 	// flags for Verify Command
 	verifyCmd.PersistentFlags().BoolVar(
 		&flagsObj.haAWSProvision,
@@ -119,6 +144,15 @@ func init() {
 		"d",
 		false,
 		"enable debugging")
+	verifySystemdServiceCreateCmd.Flags().BoolVarP(
+		&flagsObj.debug,
+		"debug",
+		"d",
+		false,
+		"enable debugging")
+
+	verifySystemdServiceCmd.AddCommand(verifySystemdServiceCreateCmd)
+	verifyCmd.AddCommand(verifySystemdServiceCmd)
 	verifyCmd.AddCommand(verifyServeCmd)
 	RootCmd.AddCommand(verifyCmd)
 }
@@ -139,10 +173,35 @@ func (v *verifyServeCmdFlow) runVerifyServeCmd(cmd *cobra.Command, args []string
 		}
 	}
 	writer.Println("Using port " + port)
-	vs := server.NewVerifyServer(port, debug)
+	vs, err := server.NewVerifyServer(port, debug)
+	if err != nil {
+		return err
+	}
 	return vs.Start()
 }
 
+func verifySystemdCreateFunc(flagsObj *verifyCmdFlags) func(cmd *cobra.Command, args []string) error {
+	return func(cmd *cobra.Command, args []string) error {
+		c := verifySystemdCreateFlow{}
+		return c.runVerifySystemdCreateCmd(cmd, args, flagsObj.debug)
+	}
+}
+
+func (v *verifySystemdCreateFlow) runVerifySystemdCreateCmd(cmd *cobra.Command, args []string, debug bool) error {
+
+	createSystemdServiceWithBinary, err := verifysystemdcreate.NewCreateSystemdService(
+		verifysystemdcreate.NewSystemdCreateUtilsImpl(),
+		BINARY_DESTINATION_FOLDER,
+		SYSTEMD_PATH,
+		debug,
+		writer,
+	)
+	if err != nil {
+		return err
+	}
+	return createSystemdServiceWithBinary.Create()
+}
+
 func verifyCmdFunc(flagsObj *verifyCmdFlags) func(cmd *cobra.Command, args []string) error {
 	return func(cmd *cobra.Command, args []string) error {
 		c := verifyCmdFlow{
diff --git a/components/automate-cli/pkg/verifyserver/logger/logger.go b/components/automate-cli/pkg/verifyserver/logger/logger.go
deleted file mode 100644
index 9d9929496..000000000
--- a/components/automate-cli/pkg/verifyserver/logger/logger.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package logger
-
-import (
-	"os"
-
-	"github.com/sirupsen/logrus"
-)
-
-type ILogger interface {
-	Info(args ...interface{})
-	Error(args ...interface{})
-	GetLevel() logrus.Level
-}
-
-type LogActions struct {
-	log logrus.Logger
-}
-
-func NewLogger(debug bool) ILogger {
-	level := logrus.InfoLevel
-	if debug {
-		level = logrus.DebugLevel
-	}
-	lg := &logrus.Logger{
-		Out:       os.Stderr,
-		Formatter: &logrus.TextFormatter{TimestampFormat: "2006-01-02 15:04:05.000", FullTimestamp: true},
-		Hooks:     make(logrus.LevelHooks),
-		Level:     level,
-	}
-	return lg
-}
-
-func (la *LogActions) Info(args ...interface{}) {
-	la.log.Info()
-}
-
-func (la *LogActions) Error(args ...interface{}) {
-	la.log.Error()
-}
-
-func (la *LogActions) GetLevel() string {
-	return la.log.GetLevel().String()
-}
diff --git a/components/automate-cli/pkg/verifyserver/rundev/main.go b/components/automate-cli/pkg/verifyserver/rundev/main.go
index e1fbcf340..370e457a4 100644
--- a/components/automate-cli/pkg/verifyserver/rundev/main.go
+++ b/components/automate-cli/pkg/verifyserver/rundev/main.go
@@ -1,8 +1,17 @@
 package main
 
-import "github.com/chef/automate/components/automate-cli/pkg/verifyserver/server"
+import (
+	"fmt"
+	"os"
+
+	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/server"
+)
 
 func main() {
-	vs := server.NewVerifyServer(server.DEFAULT_PORT, true)
+	vs, err := server.NewVerifyServer(server.DEFAULT_PORT, true)
+	if err != nil {
+		fmt.Println(err)
+		os.Exit(1)
+	}
 	vs.Start()
 }
diff --git a/components/automate-cli/pkg/verifyserver/server/api/v1/handler.go b/components/automate-cli/pkg/verifyserver/server/api/v1/handler.go
index e264a1b5f..1a5bd1366 100644
--- a/components/automate-cli/pkg/verifyserver/server/api/v1/handler.go
+++ b/components/automate-cli/pkg/verifyserver/server/api/v1/handler.go
@@ -1,16 +1,16 @@
 package v1
 
 import (
-	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/logger"
 	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/services/statusservice"
+	"github.com/chef/automate/lib/logger"
 )
 
 type Handler struct {
-	Logger        logger.ILogger
+	Logger        logger.Logger
 	StatusService statusservice.IStatusService
 }
 
-func NewHandler(logger logger.ILogger) *Handler {
+func NewHandler(logger logger.Logger) *Handler {
 	return &Handler{Logger: logger}
 }
 
diff --git a/components/automate-cli/pkg/verifyserver/server/server.go b/components/automate-cli/pkg/verifyserver/server/server.go
index 8b8718ade..ae272181b 100644
--- a/components/automate-cli/pkg/verifyserver/server/server.go
+++ b/components/automate-cli/pkg/verifyserver/server/server.go
@@ -4,10 +4,10 @@ import (
 	"strings"
 
 	"github.com/ansrivas/fiberprometheus"
-	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/logger"
 	v1 "github.com/chef/automate/components/automate-cli/pkg/verifyserver/server/api/v1"
 	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/services/statusservice"
 	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/utils/fiberutils"
+	"github.com/chef/automate/lib/logger"
 	"github.com/gofiber/cors"
 	"github.com/gofiber/fiber"
 	"github.com/gofiber/fiber/middleware"
@@ -16,32 +16,41 @@ import (
 const (
 	SERVICE      = "verify-server"
 	DEFAULT_PORT = "7799"
+	INFO_LEVEL   = "info"
+	DEBUG_LEVEL  = "debug"
 )
 
 type VerifyServer struct {
 	Port    string
-	Log     logger.ILogger
+	Log     logger.Logger
 	App     *fiber.App
 	Handler *v1.Handler
 }
 
-func NewVerifyServer(port string, debug bool) *VerifyServer {
-	log := logger.NewLogger(debug)
+func NewVerifyServer(port string, debug bool) (*VerifyServer, error) {
+	defaultLevel := INFO_LEVEL
+	if debug {
+		defaultLevel = DEBUG_LEVEL
+	}
+	l, err := logger.NewLogger("text", defaultLevel)
+	if err != nil {
+		return nil, err
+	}
 	fconf := &fiber.Settings{
 		ServerHeader: SERVICE,
 		ErrorHandler: fiberutils.CustomErrorHandler,
 	}
 	app := fiber.New(fconf)
-	handler := v1.NewHandler(log).
+	handler := v1.NewHandler(l).
 		AddStatusService(statusservice.NewStatusService())
 	vs := &VerifyServer{
 		Port:    port,
-		Log:     log,
+		Log:     l,
 		App:     app,
 		Handler: handler,
 	}
 	vs.Setup()
-	return vs
+	return vs, nil
 }
 
 func (vs *VerifyServer) Start() error {
diff --git a/components/automate-cli/pkg/verifyserver/utils/fiberutils/fiber.go b/components/automate-cli/pkg/verifyserver/utils/fiberutils/fiber.go
index 43e546a5e..ba7015a24 100644
--- a/components/automate-cli/pkg/verifyserver/utils/fiberutils/fiber.go
+++ b/components/automate-cli/pkg/verifyserver/utils/fiberutils/fiber.go
@@ -1,13 +1,13 @@
 package fiberutils
 
 import (
-	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/logger"
 	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/response"
+	"github.com/chef/automate/lib/logger"
 	"github.com/gofiber/fiber"
 	"github.com/gofiber/utils"
 )
 
-func LogResgisteredRoutes(stack [][]*fiber.Route, logger logger.ILogger) {
+func LogResgisteredRoutes(stack [][]*fiber.Route, logger logger.Logger) {
 	// Log all registered routes
 	logger.Info("List of Routes registered:")
 	registerdRoutes := map[string]*fiber.Route{}
diff --git a/components/automate-cli/pkg/verifyserver/utils/fiberutils/logconfig.go b/components/automate-cli/pkg/verifyserver/utils/fiberutils/logconfig.go
index 852264b7c..2bb42c37f 100644
--- a/components/automate-cli/pkg/verifyserver/utils/fiberutils/logconfig.go
+++ b/components/automate-cli/pkg/verifyserver/utils/fiberutils/logconfig.go
@@ -1,27 +1,41 @@
 package fiberutils
 
 import (
+	"os"
 	"strings"
 	"time"
 
-	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/logger"
+	"github.com/chef/automate/lib/logger"
 	"github.com/gofiber/fiber/middleware"
 	"github.com/sirupsen/logrus"
 )
 
-func GetLogConfig(log logger.ILogger) (lc middleware.LoggerConfig) {
+const (
+	VERIFY_SERVER_TIMEZONE = "VERIFY_SERVER_TIMEZONE"
+	DEFAULT_TIMEZONE       = "UTC"
+)
+
+func GetLogConfig(log logger.Logger) (lc middleware.LoggerConfig) {
+	timeZone := DEFAULT_TIMEZONE
+	envTimeZone := os.Getenv(VERIFY_SERVER_TIMEZONE)
+	if envTimeZone != "" {
+		if _, err := time.LoadLocation(envTimeZone); err == nil {
+			timeZone = envTimeZone
+		}
+	}
+	log.Info("Using TimeZone: " + timeZone)
 	lc = middleware.LoggerConfig{
 		TimeFormat: time.RFC3339,
-		TimeZone:   "Asia/Kolkata",
+		TimeZone:   timeZone,
 	}
-	if log.GetLevel() <= logrus.DebugLevel {
+	if log.NewEntry().Logger.GetLevel() <= logrus.DebugLevel {
 		lc.Format = generateLogFormat(
-			"${magenta}"+strings.ToUpper(log.GetLevel().String())+"${reset}",
+			"${magenta}"+strings.ToUpper(log.NewEntry().Logger.GetLevel().String())+"${reset}",
 			"time", "pid", "status", "method", "path", "latency", "error", "bytesReceived", "bytesSent", "body",
 		)
 	} else {
 		lc.Format = generateLogFormat(
-			"${green}"+strings.ToUpper(log.GetLevel().String())+"${reset}",
+			"${green}"+strings.ToUpper(log.NewEntry().Logger.GetLevel().String())+"${reset}",
 			"time", "pid", "status", "method", "path", "latency", "error", "bytesReceived", "bytesSent",
 		)
 	}
diff --git a/components/automate-cli/pkg/verifysystemdcreate/mocksystemdcreateutils.go b/components/automate-cli/pkg/verifysystemdcreate/mocksystemdcreateutils.go
new file mode 100644
index 000000000..549a80de6
--- /dev/null
+++ b/components/automate-cli/pkg/verifysystemdcreate/mocksystemdcreateutils.go
@@ -0,0 +1,21 @@
+package verifysystemdcreate
+
+type MockSystemdCreateUtils struct {
+	GetBinaryPathFunc            func() (string, error)
+	SystemdRunningFunc           func() error
+	CreateDestinationAndCopyFunc func(binarySrcPath, binaryDestPath string) error
+	ExecuteShellCommandFunc      func(name string, arg []string) error
+}
+
+func (msc *MockSystemdCreateUtils) GetBinaryPath() (string, error) {
+	return msc.GetBinaryPathFunc()
+}
+func (msc *MockSystemdCreateUtils) SystemdRunning() error {
+	return msc.SystemdRunningFunc()
+}
+func (msc *MockSystemdCreateUtils) CreateDestinationAndCopy(binarySrcPath, binaryDestPath string) error {
+	return msc.CreateDestinationAndCopyFunc(binarySrcPath, binaryDestPath)
+}
+func (msc *MockSystemdCreateUtils) ExecuteShellCommand(name string, arg []string) error {
+	return msc.ExecuteShellCommandFunc(name, arg)
+}
diff --git a/components/automate-cli/pkg/verifysystemdcreate/systemdcreatesvc.go b/components/automate-cli/pkg/verifysystemdcreate/systemdcreatesvc.go
new file mode 100644
index 000000000..793b14e2c
--- /dev/null
+++ b/components/automate-cli/pkg/verifysystemdcreate/systemdcreatesvc.go
@@ -0,0 +1,191 @@
+package verifysystemdcreate
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+	"text/template"
+
+	"github.com/chef/automate/components/automate-deployment/pkg/cli"
+	"github.com/chef/automate/lib/logger"
+	"github.com/pkg/errors"
+)
+
+const (
+	INFO_LEVEL          = "info"
+	DEBUG_LEVEL         = "debug"
+	SERVICE_NAME        = "automate-verify"
+	SERVICE_DESCRIPTION = "Service for automating verification"
+	SERVICE_COMMAND     = "chef-automate verify serve"
+	SYSTEMD_FILE        = "%s.service"
+	TEMPLATE_NAME       = "systemd-service"
+	TEMPLATE_TEXT       = `[Unit]
+Description={{.ServiceDescription}}
+After=network.target
+
+[Service]
+ExecStart={{.ServiceCommand}}
+Restart=always
+StandardOutput=journal
+StandardError=journal
+
+[Install]
+WantedBy=multi-user.target
+`
+)
+
+type systemdInput struct {
+	ServiceName        string
+	ServiceDescription string
+	ServiceCommand     string
+}
+
+type CreateSystemdService struct {
+	SystemdCreateUtils      SystemdCreateUtils
+	BinaryDestinationFolder string
+	SystemdLocation         string
+	Debug                   bool
+	Logger                  logger.Logger
+	Writer                  *cli.Writer
+}
+
+func NewCreateSystemdService(
+	systemdCreateUtils SystemdCreateUtils,
+	binaryDestinationFolder string,
+	systemdLocation string,
+	debug bool,
+	writer *cli.Writer) (*CreateSystemdService, error) {
+	level := INFO_LEVEL
+	if debug {
+		level = DEBUG_LEVEL
+	}
+	l, err := logger.NewLoggerWithOut("text", level, writer)
+	if err != nil {
+		return nil, err
+	}
+	if systemdCreateUtils == nil {
+		return nil, errors.New("SystemdCreateUtils cannot be nil")
+	}
+	if writer == nil {
+		return nil, errors.New("Writer cannot be nil")
+	}
+	if binaryDestinationFolder == "" {
+		return nil, errors.New("Binary destination folder cannot be empty")
+	}
+	if systemdLocation == "" {
+		return nil, errors.New("Systemd location cannot be empty")
+	}
+	return &CreateSystemdService{
+		SystemdCreateUtils:      systemdCreateUtils,
+		BinaryDestinationFolder: binaryDestinationFolder,
+		SystemdLocation:         systemdLocation,
+		Logger:                  l,
+		Writer:                  writer,
+	}, nil
+}
+
+// Create the systemd service file.
+func (css *CreateSystemdService) createSystemdServiceFile() error {
+	// Create the template.
+	tmpl, err := template.New(TEMPLATE_NAME).Parse(TEMPLATE_TEXT)
+	if err != nil {
+		return errors.Wrap(err, "Error parsing template")
+	}
+
+	// Prepare the template data.
+	data := systemdInput{
+		ServiceName:        SERVICE_NAME,
+		ServiceDescription: SERVICE_DESCRIPTION,
+		ServiceCommand:     css.BinaryDestinationFolder + "/" + SERVICE_COMMAND,
+	}
+
+	css.Logger.Debugf(
+		"Template data: ServiceName %s, ServiceDescription %s, ServiceCommand %s",
+		data.ServiceName, data.ServiceDescription, data.ServiceCommand,
+	)
+
+	// Execute the template to generate the service file.
+	serviceFilePath := fmt.Sprintf(css.SystemdLocation+"/"+SYSTEMD_FILE, SERVICE_NAME)
+	css.Logger.Debugf("Service file path %s", serviceFilePath)
+	serviceFile, err := os.Create(serviceFilePath)
+	if err != nil {
+		return errors.Wrap(err, "Error creating service file")
+	}
+	defer serviceFile.Close()
+
+	err = tmpl.Execute(serviceFile, data)
+	if err != nil {
+		return errors.Wrap(err, "Error executing template")
+	}
+
+	return nil
+}
+
+// Enable and start the systemd service.
+func (css *CreateSystemdService) enableSystemdService() error {
+	service := fmt.Sprintf(SYSTEMD_FILE, SERVICE_NAME)
+	err := css.SystemdCreateUtils.ExecuteShellCommand("systemctl", []string{"daemon-reload"})
+	if err != nil {
+		return errors.Wrap(err, "Error reloading systemd daemon")
+	}
+	css.Logger.Debugln("Reloaded systemd daemon")
+	err = css.SystemdCreateUtils.ExecuteShellCommand("systemctl", []string{"enable", service})
+	if err != nil {
+		return errors.Wrap(err, "Error enabling service")
+	}
+	css.Logger.Debugln("Enabled service successfully")
+	err = css.SystemdCreateUtils.ExecuteShellCommand("systemctl", []string{"start", service})
+	if err != nil {
+		return errors.Wrap(err, "Error starting service")
+	}
+	css.Logger.Debugln("Started service successfully")
+	return nil
+}
+
+func (css *CreateSystemdService) isSystemdEnabled() error {
+	return css.SystemdCreateUtils.ExecuteShellCommand("systemctl", []string{"is-enabled", fmt.Sprintf(SYSTEMD_FILE, SERVICE_NAME)})
+}
+
+// Create and start the systemd service.
+func (css *CreateSystemdService) Create() error {
+
+	currentBinaryPath, err := css.SystemdCreateUtils.GetBinaryPath()
+	css.Logger.Debugf("Current binary path %s", currentBinaryPath)
+	if err != nil {
+		return err
+	}
+	err = css.SystemdCreateUtils.SystemdRunning()
+	if err != nil {
+		return err
+	}
+	css.Logger.Debugln("Systemd is found on this system")
+
+	err = css.isSystemdEnabled()
+	if err == nil {
+		return errors.New(fmt.Sprintf("Service %[1]v already exists on this node, use systemctl start/stop/status %[1]v", SERVICE_NAME))
+	}
+	css.Logger.Debugf("Service %s is not running on this system", SERVICE_NAME)
+
+	fullBinaryDestination := filepath.Join(css.BinaryDestinationFolder, filepath.Base(currentBinaryPath))
+	css.Logger.Debugf("Full binary destination path %s", fullBinaryDestination)
+	err = css.SystemdCreateUtils.CreateDestinationAndCopy(currentBinaryPath, fullBinaryDestination)
+	if err != nil {
+		return err
+	}
+
+	css.Writer.Printf("Binary copied to %s\n", fullBinaryDestination)
+
+	err = css.createSystemdServiceFile()
+	if err != nil {
+		return err
+	}
+
+	err = css.enableSystemdService()
+	if err != nil {
+		return err
+	}
+
+	css.Writer.Printf("Service %s created successfully\n", SERVICE_NAME)
+
+	return nil
+}
diff --git a/components/automate-cli/pkg/verifysystemdcreate/systemdcreateutils.go b/components/automate-cli/pkg/verifysystemdcreate/systemdcreateutils.go
new file mode 100644
index 000000000..ae7695f47
--- /dev/null
+++ b/components/automate-cli/pkg/verifysystemdcreate/systemdcreateutils.go
@@ -0,0 +1,54 @@
+package verifysystemdcreate
+
+import (
+	"os"
+	"os/exec"
+	"path/filepath"
+
+	"github.com/chef/automate/components/automate-cli/pkg/status"
+	"github.com/chef/automate/components/automate-deployment/pkg/target"
+	"github.com/chef/automate/lib/io/fileutils"
+)
+
+type SystemdCreateUtils interface {
+	GetBinaryPath() (string, error)
+	SystemdRunning() error
+	CreateDestinationAndCopy(binarySrcPath, binaryDestPath string) error
+	ExecuteShellCommand(name string, arg []string) error
+}
+
+type SystemdCreateUtilsImpl struct{}
+
+func NewSystemdCreateUtilsImpl() SystemdCreateUtils {
+	return &SystemdCreateUtilsImpl{}
+}
+
+func (scu *SystemdCreateUtilsImpl) GetBinaryPath() (string, error) {
+	binaryPath, err := os.Executable()
+	if err != nil {
+		return "", status.Wrap(err, status.UnknownError, "Error getting executable path")
+	}
+	binaryPath, err = filepath.EvalSymlinks(binaryPath)
+	if err != nil {
+		return "", status.Wrap(err, status.UnknownError, "Error evaluating symlinks in binary path")
+	}
+	return binaryPath, nil
+}
+
+func (scu *SystemdCreateUtilsImpl) SystemdRunning() error {
+	usingSystemd, err := target.NewLocalTarget(true).SystemdRunning()
+	if err != nil {
+		return status.Wrap(err, status.UnknownError, "Error checking systemd present or not")
+	}
+	if !usingSystemd {
+		return status.New(status.UnknownError, "Cannot create automate-verify service since systemd is not present on this machine")
+	}
+	return nil
+}
+func (scu *SystemdCreateUtilsImpl) CreateDestinationAndCopy(binarySrcPath, binaryDestPath string) error {
+	return fileutils.CreateDestinationAndCopy(binarySrcPath, binaryDestPath)
+}
+
+func (scu *SystemdCreateUtilsImpl) ExecuteShellCommand(name string, arg []string) error {
+	return exec.Command(name, arg...).Run()
+}
diff --git a/components/docs-chef-io/content/automate/ha_onprim_deployment_procedure.md b/components/docs-chef-io/content/automate/ha_onprim_deployment_procedure.md
index 16e3e9c5f..a27d55a66 100644
--- a/components/docs-chef-io/content/automate/ha_onprim_deployment_procedure.md
+++ b/components/docs-chef-io/content/automate/ha_onprim_deployment_procedure.md
@@ -33,7 +33,7 @@ This section will discuss the steps to deploy Chef Automate HA on-premise machin
 - Key-based SSH for the provisioning user for all the machines for HA-Deployment.
 - We do not support passphrases for Private Key authentication.
 - LoadBalancers are set up according to [Chef Automate HA Architecture](/automate/ha/) needs as explained in [Load Balancer Configuration page](/automate/loadbalancer_configuration/).
-- Network ports are opened as per [Chef Automate Architecture](/automate/ha/) needs as explained in [Security and Firewall page](/automate/ha_security_firewall/)
+- Network ports are opened as per [Chef Automate Architecture](/automate/ha/) needs as explained in [Security and Firewall page](/automate/ha_on_premises_deployment_prerequisites/#firewall-checks)
 - Make sure your linux has `sysctl` utility available in all Machines.
 - DNS is configured to redirect `chefautomate.example.com` to the Primary Load Balancer.
 - DNS is configured to redirect `chefinfraserver.example.com` to the Primary Load Balancer.
diff --git a/lib/io/fileutils/copy_file.go b/lib/io/fileutils/copy_file.go
index c54cc531e..d6bc020a2 100644
--- a/lib/io/fileutils/copy_file.go
+++ b/lib/io/fileutils/copy_file.go
@@ -76,3 +76,10 @@ func copyFile(srcPath, dstPath string, opts *copyOpts) error {
 
 	return AtomicWrite(dstPath, src, WithAtomicWriteFileMode(srcInfo.Mode()))
 }
+
+func CreateDestinationAndCopy(binarySrcPath, binaryDestPath string) error {
+	if err := os.MkdirAll(filepath.Dir(binaryDestPath), 0755); err != nil {
+		return errors.Wrap(err, "Error creating destination folder")
+	}
+	return CopyFile(binarySrcPath, binaryDestPath, Overwrite())
+}


Test patch:
diff --git a/components/automate-cli/pkg/verifyserver/server/api/v1/status_test.go b/components/automate-cli/pkg/verifyserver/server/api/v1/status_test.go
index 3f50232f5..ca63c9cf7 100644
--- a/components/automate-cli/pkg/verifyserver/server/api/v1/status_test.go
+++ b/components/automate-cli/pkg/verifyserver/server/api/v1/status_test.go
@@ -5,12 +5,12 @@ import (
 	"net/http/httptest"
 	"testing"
 
-	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/logger"
 	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/models"
 	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/server"
 	v1 "github.com/chef/automate/components/automate-cli/pkg/verifyserver/server/api/v1"
 	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/services/statusservice"
 	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/utils/fiberutils"
+	"github.com/chef/automate/lib/logger"
 	"github.com/gofiber/fiber"
 
 	"github.com/stretchr/testify/assert"
@@ -30,8 +30,11 @@ func SetupMockStatusService() statusservice.IStatusService {
 	}
 }
 
-func SetupDefaultHandlers(ss statusservice.IStatusService) *fiber.App {
-	log := logger.NewLogger(true)
+func SetupDefaultHandlers(ss statusservice.IStatusService) (*fiber.App, error) {
+	log, err := logger.NewLogger("text", "debug")
+	if err != nil {
+		return nil, err
+	}
 	fconf := &fiber.Settings{
 		ServerHeader: server.SERVICE,
 		ErrorHandler: fiberutils.CustomErrorHandler,
@@ -46,7 +49,7 @@ func SetupDefaultHandlers(ss statusservice.IStatusService) *fiber.App {
 		Handler: handler,
 	}
 	vs.Setup()
-	return vs.App
+	return vs.App, nil
 }
 
 func TestStatusAPI(t *testing.T) {
@@ -63,7 +66,8 @@ func TestStatusAPI(t *testing.T) {
 	}
 	statusEndpoint := "/status"
 	// Setup the app as it is done in the main function
-	app := SetupDefaultHandlers(SetupMockStatusService())
+	app, err := SetupDefaultHandlers(SetupMockStatusService())
+	assert.NoError(t, err)
 
 	for _, test := range tests {
 		t.Run(test.description, func(t *testing.T) {
diff --git a/components/automate-cli/pkg/verifyserver/utils/fiberutils/fiber_test.go b/components/automate-cli/pkg/verifyserver/utils/fiberutils/fiber_test.go
new file mode 100644
index 000000000..8af01bead
--- /dev/null
+++ b/components/automate-cli/pkg/verifyserver/utils/fiberutils/fiber_test.go
@@ -0,0 +1,74 @@
+package fiberutils_test
+
+import (
+	"errors"
+	"io/ioutil"
+	"net/http"
+	"testing"
+
+	"github.com/chef/automate/lib/logger"
+	"github.com/chef/automate/lib/majorupgrade_utils"
+	"github.com/gofiber/fiber"
+	"github.com/stretchr/testify/assert"
+
+	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/utils/fiberutils"
+)
+
+func TestLogResgisteredRoutes(t *testing.T) {
+	// Create a buffer to capture log output
+	cw := majorupgrade_utils.NewCustomWriter()
+	logger, err := logger.NewLoggerWithOut("text", "info", cw.CliWriter)
+	assert.NoError(t, err)
+
+	// Create a mock stack of registered routes
+	stack := [][]*fiber.Route{
+		{
+			{Method: "GET", Path: "/foo"},
+			{Method: "POST", Path: "/foo"},
+			{Method: "GET", Path: "/bar"},
+		},
+		{
+			{Method: "POST", Path: "/baz"},
+			{Method: "PUT", Path: "/baz"},
+		},
+	}
+
+	// Call the function under test
+	fiberutils.LogResgisteredRoutes(stack, logger)
+
+	// Check the log output
+	assert.Contains(t, cw.Output(), "GET /foo")
+	assert.Contains(t, cw.Output(), "POST /foo", cw.Output())
+	assert.Contains(t, cw.Output(), "GET /bar", cw.Output())
+	assert.Contains(t, cw.Output(), "POST /baz", cw.Output())
+	assert.Contains(t, cw.Output(), "PUT /baz", cw.Output())
+}
+
+func TestCustomErrorHandler(t *testing.T) {
+	app := fiber.New()
+
+	// Register a route that always returns an error
+	app.Get("/panic", func(ctx *fiber.Ctx) {
+		defer func() {
+			if err := recover(); err != nil {
+				ctx.Next(errors.New(err.(string)))
+			}
+		}()
+		panic("test panic")
+	})
+
+	// Replace the default error handler with CustomErrorHandler
+	app.Settings.ErrorHandler = func(ctx *fiber.Ctx, err error) {
+		fiberutils.CustomErrorHandler(ctx, err)
+	}
+
+	// Make a request to the error route and assert that the response is correct
+	req, _ := http.NewRequest(http.MethodGet, "/panic", nil)
+	resp, err := app.Test(req)
+
+	assert.NoError(t, err, "app.Test should not return an error")
+	assert.Equal(t, http.StatusInternalServerError, resp.StatusCode, "response status should be 500")
+	body, err := ioutil.ReadAll(resp.Body)
+	assert.NoError(t, err)
+	assert.Contains(t, "{\"status\":\"FAILED\",\"result\":null,\"error\":{\"code\":500,\"message\":\"Internal Server Error\"}}", string(body))
+}
diff --git a/components/automate-cli/pkg/verifyserver/utils/fiberutils/logconfig_test.go b/components/automate-cli/pkg/verifyserver/utils/fiberutils/logconfig_test.go
new file mode 100644
index 000000000..14f95a79e
--- /dev/null
+++ b/components/automate-cli/pkg/verifyserver/utils/fiberutils/logconfig_test.go
@@ -0,0 +1,53 @@
+package fiberutils_test
+
+import (
+	"os"
+	"testing"
+
+	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/utils/fiberutils"
+	"github.com/chef/automate/lib/logger"
+	"github.com/chef/automate/lib/majorupgrade_utils"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestSystemdCreateUtils(t *testing.T) {
+	l, err := logger.NewLogger("text", "debug")
+	assert.NoError(t, err)
+	cw := majorupgrade_utils.NewCustomWriter()
+
+	t.Run("It should create the debug format in case of debug level", func(t *testing.T) {
+		lc := fiberutils.GetLogConfig(l)
+		assert.Contains(t, lc.Format, "| ${magenta}DEBUG${reset} | ${red}${time}${reset}  |  ${green}${pid}${reset}  |"+
+			"  ${yellow}${status}${reset}  |  ${blue}${method}${reset}  |  ${magenta}${path}${reset}  |"+
+			"  ${cyan}${latency}${reset}  |  ${white}${error}${reset}  |  ${red}${bytesReceived}${reset}  |"+
+			"  ${green}${bytesSent}${reset}  |  ${yellow}${body}${reset}  |  \n")
+	})
+	t.Run("It should create the info format in case of trace level", func(t *testing.T) {
+		l, err := logger.NewLogger("text", "trace")
+		assert.NoError(t, err)
+		lc := fiberutils.GetLogConfig(l)
+		assert.Contains(t, lc.Format, "| ${green}TRACE${reset} | ${red}${time}${reset}  |  ${green}${pid}${reset}  |"+
+			"  ${yellow}${status}${reset}  |  ${blue}${method}${reset}  |  ${magenta}${path}${reset}  |"+
+			"  ${cyan}${latency}${reset}  |  ${white}${error}${reset}  |  ${red}${bytesReceived}${reset}  |"+
+			"  ${green}${bytesSent}${reset}  |  \n")
+	})
+	t.Run("It should print the selected timezone", func(t *testing.T) {
+		l, err := logger.NewLoggerWithOut("text", "debug", cw.CliWriter)
+		assert.NoError(t, err)
+		fiberutils.GetLogConfig(l)
+		assert.Contains(t, cw.Output(), "Using TimeZone: UTC")
+	})
+	t.Run("It should use UTC if timezone format is incorrect", func(t *testing.T) {
+		os.Setenv(fiberutils.VERIFY_SERVER_TIMEZONE, "GAMU")
+		defer os.Unsetenv(fiberutils.VERIFY_SERVER_TIMEZONE)
+		lc := fiberutils.GetLogConfig(l)
+		assert.Contains(t, lc.TimeZone, fiberutils.DEFAULT_TIMEZONE)
+	})
+	t.Run("It should use timezone provided its correct", func(t *testing.T) {
+		testTimeZone := "America/New_York"
+		os.Setenv(fiberutils.VERIFY_SERVER_TIMEZONE, testTimeZone)
+		defer os.Unsetenv(fiberutils.VERIFY_SERVER_TIMEZONE)
+		lc := fiberutils.GetLogConfig(l)
+		assert.Contains(t, lc.TimeZone, testTimeZone)
+	})
+}
diff --git a/components/automate-cli/pkg/verifysystemdcreate/systemdcreatesvc_test.go b/components/automate-cli/pkg/verifysystemdcreate/systemdcreatesvc_test.go
new file mode 100644
index 000000000..1caf059f4
--- /dev/null
+++ b/components/automate-cli/pkg/verifysystemdcreate/systemdcreatesvc_test.go
@@ -0,0 +1,214 @@
+package verifysystemdcreate_test
+
+import (
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"testing"
+
+	"github.com/chef/automate/components/automate-cli/pkg/verifysystemdcreate"
+	"github.com/chef/automate/lib/majorupgrade_utils"
+	"github.com/stretchr/testify/assert"
+)
+
+func setupCopy(t *testing.T) (string, string) {
+	tmpDir := t.TempDir()
+	tmpDir2 := t.TempDir()
+	return tmpDir, tmpDir2
+}
+
+func TestSystemdServiceCreate(t *testing.T) {
+	cw := majorupgrade_utils.NewCustomWriter()
+	createDestinationAndCopy := func(binarySrcPath, binaryDestPath string) error {
+		return nil
+	}
+	executeShellCommand := func(name string, arg []string) error {
+		if arg[0] == "is-enabled" {
+			return errors.New("Failed to get unit file state for automate-verify.service: No such file or directory")
+		}
+		return nil
+	}
+
+	executeShellCommandPassIsEnabled := func(name string, arg []string) error {
+		return nil
+	}
+
+	executeShellCommandErr := func(name string, arg []string) error {
+		if arg[0] == "is-enabled" {
+			return errors.New("Failed to get unit file state for automate-verify.service: No such file or directory")
+		}
+		return errors.New("systemctl command not found")
+	}
+
+	executeShellCommandStartErr := func(name string, arg []string) error {
+		if arg[0] == "is-enabled" {
+			return errors.New("Failed to get unit file state for automate-verify.service: No such file or directory")
+		} else if arg[0] == "start" {
+			return errors.New("systemctl command not found")
+		}
+		return nil
+	}
+
+	executeShellCommandEnableErr := func(name string, arg []string) error {
+		if arg[0] == "is-enabled" {
+			return errors.New("Failed to get unit file state for automate-verify.service: No such file or directory")
+		} else if arg[0] == "enable" {
+			return errors.New("systemctl command not found")
+		}
+		return nil
+	}
+
+	setupMockUtils := func() *verifysystemdcreate.MockSystemdCreateUtils {
+		return &verifysystemdcreate.MockSystemdCreateUtils{
+			GetBinaryPathFunc:            func() (string, error) { return "", nil },
+			SystemdRunningFunc:           func() error { return nil },
+			CreateDestinationAndCopyFunc: createDestinationAndCopy,
+			ExecuteShellCommandFunc:      executeShellCommand,
+		}
+	}
+
+	t.Run("it gives error if binary destination directory is empty", func(t *testing.T) {
+		_, systemdLocation := setupCopy(t)
+		_, err := verifysystemdcreate.NewCreateSystemdService(setupMockUtils(), "", systemdLocation, false, cw.CliWriter)
+		assert.Error(t, err)
+		assert.Equal(t, "Binary destination folder cannot be empty", err.Error())
+	})
+
+	t.Run("it gives error if systemd location is empty", func(t *testing.T) {
+		binaryDestinationFolder, _ := setupCopy(t)
+		_, err := verifysystemdcreate.NewCreateSystemdService(setupMockUtils(), binaryDestinationFolder, "", false, cw.CliWriter)
+		assert.Error(t, err)
+		assert.Equal(t, "Systemd location cannot be empty", err.Error())
+	})
+
+	t.Run("it gives error if systemd create utils is nil", func(t *testing.T) {
+		binaryDestinationFolder, _ := setupCopy(t)
+		_, err := verifysystemdcreate.NewCreateSystemdService(nil, binaryDestinationFolder, "", false, cw.CliWriter)
+		assert.Error(t, err)
+		assert.Equal(t, "SystemdCreateUtils cannot be nil", err.Error())
+	})
+
+	t.Run("it gives error if writer is nil", func(t *testing.T) {
+		binaryDestinationFolder, _ := setupCopy(t)
+		_, err := verifysystemdcreate.NewCreateSystemdService(setupMockUtils(), binaryDestinationFolder, "", false, nil)
+		assert.Error(t, err)
+		assert.Equal(t, "Writer cannot be nil", err.Error())
+	})
+
+	t.Run("it creates automate-verify.service file", func(t *testing.T) {
+		binaryDestinationFolder, systemdLocation := setupCopy(t)
+		vsc, err := verifysystemdcreate.NewCreateSystemdService(setupMockUtils(), binaryDestinationFolder, systemdLocation, false, cw.CliWriter)
+		assert.NoError(t, err)
+		err = vsc.Create()
+		assert.NoError(t, err)
+		_, err = os.Stat(systemdLocation + "/automate-verify.service")
+		assert.NoError(t, err)
+		assert.Contains(t, cw.Output(), "Binary copied to "+binaryDestinationFolder)
+		assert.Contains(t, cw.Output(), "Service automate-verify created successfully")
+	})
+
+	t.Run("it gives error if there automate-verify systemd-service is already enabled", func(t *testing.T) {
+		mockUtils := setupMockUtils()
+		mockUtils.ExecuteShellCommandFunc = executeShellCommandPassIsEnabled
+		binaryDestinationFolder, systemdLocation := setupCopy(t)
+		vsc, err := verifysystemdcreate.NewCreateSystemdService(mockUtils, binaryDestinationFolder, systemdLocation, false, cw.CliWriter)
+		assert.NoError(t, err)
+		err = vsc.Create()
+		assert.Error(t, err)
+		assert.Equal(t, "Service automate-verify already exists on this node, use systemctl start/stop/status automate-verify", err.Error())
+	})
+
+	t.Run("it gives error if there is problem in running shell command", func(t *testing.T) {
+		mockUtils := setupMockUtils()
+		mockUtils.ExecuteShellCommandFunc = executeShellCommandErr
+		binaryDestinationFolder, systemdLocation := setupCopy(t)
+		vsc, err := verifysystemdcreate.NewCreateSystemdService(mockUtils, binaryDestinationFolder, systemdLocation, false, cw.CliWriter)
+		assert.NoError(t, err)
+		err = vsc.Create()
+		assert.Error(t, err)
+		assert.Equal(t, "Error reloading systemd daemon: systemctl command not found", err.Error())
+	})
+
+	t.Run("it gives error if there is problem in running systemctl enable command", func(t *testing.T) {
+		mockUtils := setupMockUtils()
+		mockUtils.ExecuteShellCommandFunc = executeShellCommandEnableErr
+		binaryDestinationFolder, systemdLocation := setupCopy(t)
+		vsc, err := verifysystemdcreate.NewCreateSystemdService(mockUtils, binaryDestinationFolder, systemdLocation, false, cw.CliWriter)
+		assert.NoError(t, err)
+		err = vsc.Create()
+		assert.Error(t, err)
+		assert.Equal(t, "Error enabling service: systemctl command not found", err.Error())
+	})
+
+	t.Run("it gives error if there is problem in running systemctl start command", func(t *testing.T) {
+		mockUtils := setupMockUtils()
+		mockUtils.ExecuteShellCommandFunc = executeShellCommandStartErr
+		binaryDestinationFolder, systemdLocation := setupCopy(t)
+		vsc, err := verifysystemdcreate.NewCreateSystemdService(mockUtils, binaryDestinationFolder, systemdLocation, false, cw.CliWriter)
+		assert.NoError(t, err)
+		err = vsc.Create()
+		assert.Error(t, err)
+		assert.Equal(t, "Error starting service: systemctl command not found", err.Error())
+	})
+
+	t.Run("it gives error if there is problem in createDestinationAndCopy", func(t *testing.T) {
+		mockUtils := setupMockUtils()
+		mockUtils.ExecuteShellCommandFunc = executeShellCommand
+		mockUtils.SystemdRunningFunc = func() error { return errors.New("Error in copying") }
+		binaryDestinationFolder, systemdLocation := setupCopy(t)
+		vsc, err := verifysystemdcreate.NewCreateSystemdService(mockUtils, binaryDestinationFolder, systemdLocation, false, cw.CliWriter)
+		assert.NoError(t, err)
+		err = vsc.Create()
+		assert.Error(t, err)
+		assert.Equal(t, "Error in copying", err.Error())
+	})
+
+	t.Run("it overwrites automate-verify.service file if it already exists", func(t *testing.T) {
+		mockUtils := setupMockUtils()
+		binaryDestinationFolder, systemdLocation := setupCopy(t)
+		systemdFilePath := systemdLocation + "/automate-verify.service"
+		srcData := []byte("test data")
+		assert.NoError(t, ioutil.WriteFile(systemdFilePath, srcData, 0700))
+		vsc, err := verifysystemdcreate.NewCreateSystemdService(mockUtils, binaryDestinationFolder, systemdLocation, false, cw.CliWriter)
+		assert.NoError(t, err)
+		err = vsc.Create()
+		assert.NoError(t, err)
+		_, err = os.Stat(systemdFilePath)
+		assert.NoError(t, err)
+		dstData, err := ioutil.ReadFile(systemdFilePath)
+		assert.NoError(t, err)
+		assert.Contains(t, string(dstData), "Description=Service for automating verification")
+		assert.Contains(t, cw.Output(), "Binary copied to "+binaryDestinationFolder+"\nService automate-verify created successfully\n")
+	})
+
+	t.Run("it creates automate-verify.service file with proper content inside", func(t *testing.T) {
+		mockUtils := setupMockUtils()
+		binaryDestinationFolder, systemdLocation := setupCopy(t)
+		systemdFilePath := systemdLocation + "/automate-verify.service"
+		expectedSystemdContents := `[Unit]
+Description=Service for automating verification
+After=network.target
+
+[Service]
+ExecStart=%s/chef-automate verify serve
+Restart=always
+StandardOutput=journal
+StandardError=journal
+
+[Install]
+WantedBy=multi-user.target
+`
+		vsc, err := verifysystemdcreate.NewCreateSystemdService(mockUtils, binaryDestinationFolder, systemdLocation, false, cw.CliWriter)
+		assert.NoError(t, err)
+		err = vsc.Create()
+		assert.NoError(t, err)
+		_, err = os.Stat(systemdFilePath)
+		assert.NoError(t, err)
+		dstData, err := ioutil.ReadFile(systemdFilePath)
+		assert.NoError(t, err)
+		assert.Equal(t, fmt.Sprintf(expectedSystemdContents, binaryDestinationFolder), string(dstData))
+		assert.Contains(t, cw.Output(), "Binary copied to "+binaryDestinationFolder+"\nService automate-verify created successfully\n")
+	})
+
+}
diff --git a/components/automate-cli/pkg/verifysystemdcreate/systemdcreateutils_test.go b/components/automate-cli/pkg/verifysystemdcreate/systemdcreateutils_test.go
new file mode 100644
index 000000000..54f5f0777
--- /dev/null
+++ b/components/automate-cli/pkg/verifysystemdcreate/systemdcreateutils_test.go
@@ -0,0 +1,30 @@
+package verifysystemdcreate_test
+
+import (
+	"testing"
+
+	"github.com/chef/automate/components/automate-cli/pkg/verifysystemdcreate"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestSystemdCreateUtils(t *testing.T) {
+	scu := verifysystemdcreate.NewSystemdCreateUtilsImpl()
+	t.Run("GetBinaryPath should not give error", func(t *testing.T) {
+		_, err := scu.GetBinaryPath()
+		assert.NoError(t, err)
+	})
+	t.Run("ExecuteShellCommand should not give error", func(t *testing.T) {
+		err := scu.ExecuteShellCommand("ls", []string{})
+		assert.NoError(t, err)
+	})
+	t.Run("ExecuteShellCommand should not give error", func(t *testing.T) {
+		err := scu.CreateDestinationAndCopy("", "")
+		assert.Error(t, err)
+		assert.Contains(t, "read .: is a directory", err.Error())
+	})
+	t.Run("ExecuteShellCommand should not give error", func(t *testing.T) {
+		err := scu.SystemdRunning()
+		assert.Error(t, err)
+		assert.Contains(t, "Cannot create automate-verify service since systemd is not present on this machine", err.Error())
+	})
+}
diff --git a/lib/io/fileutils/copy_file_test.go b/lib/io/fileutils/copy_file_test.go
index 0228950e8..5656c04a1 100644
--- a/lib/io/fileutils/copy_file_test.go
+++ b/lib/io/fileutils/copy_file_test.go
@@ -2,7 +2,9 @@ package fileutils_test
 
 import (
 	"io/ioutil"
+	"os"
 	"path"
+	"path/filepath"
 	"testing"
 
 	"github.com/stretchr/testify/require"
@@ -58,3 +60,53 @@ func setupCopy(t *testing.T) (string, string) {
 
 	return tmpDir, tmpDir2
 }
+
+func TestCreateDestinationAndCopy(t *testing.T) {
+	t.Run("it copies the file and creates destination", func(t *testing.T) {
+		d1, d2 := setupCopy(t)
+
+		srcData := []byte("test data")
+		srcPath := path.Join(d1, "src")
+		dstPath := path.Join(d2, "testpath/one/dst")
+		require.NoError(t, ioutil.WriteFile(srcPath, srcData, 0700))
+		require.NoError(t, fileutils.CreateDestinationAndCopy(srcPath, dstPath))
+		dstData, err := ioutil.ReadFile(dstPath)
+		require.NoError(t, err)
+		require.Equal(t, srcData, dstData)
+	})
+
+	t.Run("it copies even when file exists", func(t *testing.T) {
+		d1, d2 := setupCopy(t)
+
+		srcData := []byte("src data")
+		srcPath := path.Join(d1, "src")
+		dstPath := path.Join(d2, "testpath/one/dst")
+		dstData := []byte("dst data")
+		require.NoError(t, ioutil.WriteFile(srcPath, srcData, 0700))
+		require.NoError(t, os.MkdirAll(filepath.Dir(dstPath), 0755))
+		require.NoError(t, ioutil.WriteFile(dstPath, dstData, 0700))
+		require.NoError(t, fileutils.CreateDestinationAndCopy(srcPath, dstPath))
+		dstData, err := ioutil.ReadFile(dstPath)
+		require.NoError(t, err)
+		require.Equal(t, srcData, dstData)
+	})
+
+	t.Run("it make sure that the destination file has same permissions as the source file", func(t *testing.T) {
+		d1, d2 := setupCopy(t)
+
+		srcData := []byte("src data")
+		srcPath := path.Join(d1, "src")
+		dstPath := path.Join(d2, "testpath/one/dst")
+		require.NoError(t, ioutil.WriteFile(srcPath, srcData, 0755))
+		require.NoError(t, os.MkdirAll(filepath.Dir(dstPath), 0755))
+		require.NoError(t, fileutils.CreateDestinationAndCopy(srcPath, dstPath))
+		dstData, err := ioutil.ReadFile(dstPath)
+		require.NoError(t, err)
+		require.Equal(t, srcData, dstData)
+		srcInfo, err := os.Stat(srcPath)
+		require.NoError(t, err)
+		destInfo, err := os.Stat(dstPath)
+		require.NoError(t, err)
+		require.Equal(t, srcInfo.Mode(), destInfo.Mode())
+	})
+}