Skip to main content

Command Palette

Search for a command to run...

ServoOps: Ultimate Service Management API

Monitoring and Managing System Services

Published
4 min read
ServoOps: Ultimate Service Management API

As an aspiring DevOps engineer, developing tools that streamline and automate processes is crucial. Today, we're diving into a project that exemplifies this principle: ServoOps. This robust service management API, built with Go and the Gin framework, offers an array of functionalities for managing system services efficiently. Whether you need to monitor services, start or stop them, or even kill processes by their PID, ServoOps has you covered.

Project Overview: ServoOps

ServoOps is designed to provide comprehensive service management capabilities via a RESTful API. Its key features include:

  • Service Monitoring: Fetch detailed information about running services.

  • Service Control: Start and stop services on demand.

  • Process Termination: Kill processes by their PID.

  • Health Checks: Monitor the API's health status.

  • Security: Implement basic authentication.

  • Logging: Log all API requests for auditing purposes.

Prerequisites

Before we begin, ensure you have the following installed:

  • Go (version 1.16 or higher)

  • Git

  • A Unix-based system (Linux or macOS)

Why ServoOps?

Service management is a critical aspect of system administration and DevOps practices. ServoOps simplifies this process by providing a RESTful API that interacts with system services, making it easier to monitor and control them. Whether you're managing a few services on a personal server or hundreds in a production environment, ServoOps provides a consistent and reliable interface.

Key Features

  1. Service Listing: View detailed information about all running services.

  2. Start and Stop Services: Control services with ease.

  3. Kill Services by PID: Forcefully terminate services when necessary.

  4. Middleware for Authentication and Logging: Secure and monitor API usage.

  5. Health Check Endpoint: Ensure the API is up and running.

Exploring the Code

Setting Up the Service Structure

The first step is to define a structure that holds the details of each service. This includes the process ID, name, user, memory usage, CPU usage, and start time.

goCopy codetype svc struct {
    PID         int    `json:"pid"`
    Name        string `json:"name"`
    User        string `json:"user"`
    MemoryUsage string `json:"memory_usage"`
    CPUUsage    string `json:"cpu_usage"`
    StartTime   string `json:"start_time"`
}

Executing System Commands

To interact with system services, we need a function to execute shell commands and capture their output.

goCopy codefunc executeCommand(command string) (string, error) {
    cmd := exec.Command("bash", "-c", command)
    output, err := cmd.Output()
    if err != nil {
        return "", errors.New("failed to execute command")
    }
    return string(output), nil
}

Updating Service List

We periodically update the list of running services using the ps command, which provides information about all running processes.

goCopy codefunc makestruct() {
    output, err := executeCommand("ps axo pid,comm,user,%mem,%cpu,lstart")
    if err != nil {
        fmt.Println("Failed to execute command:", err)
        return
    }

    lines := strings.Split(output, "\n")
    var newSvcs []svc

    for _, line := range lines[1:] {
        fields := strings.Fields(line)
        if len(fields) < 6 {
            continue
        }
        pid, err := strconv.Atoi(fields[0])
        if err != nil {
            fmt.Printf("Failed to parse PID: %s\n", fields[0])
            continue
        }
        name := fields[1]
        user := fields[2]
        memoryUsage := fields[3]
        cpuUsage := fields[4]
        startTime := strings.Join(fields[5:], " ")
        newSvcs = append(newSvcs, svc{PID: pid, Name: name, User: user, MemoryUsage: memoryUsage, CPUUsage: cpuUsage, StartTime: startTime})
    }

    svcs = newSvcs
    fmt.Println("Services updated")
}

Starting and Stopping Services

ServoOps allows you to start and stop services using the system's systemctl command.

goCopy codefunc startService(c *gin.Context) {
    serviceName := c.Param("name")
    output, err := executeCommand("systemctl start " + serviceName)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"message": "failed to start service", "error": err.Error()})
        return
    }
    c.JSON(http.StatusOK, gin.H{"message": "service started", "output": output})
}

func stopService(c *gin.Context) {
    serviceName := c.Param("name")
    output, err := executeCommand("systemctl stop " + serviceName)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"message": "failed to stop service", "error": err.Error()})
        return
    }
    c.JSON(http.StatusOK, gin.H{"message": "service stopped", "output": output})
}

Killing Services by PID

Sometimes, you need to forcefully terminate a service. ServoOps provides an endpoint to kill a service by its process ID.

goCopy codefunc svckill(id int) {
    fmt.Println(executeCommand("kill -9 " + strconv.Itoa(id)))
}

func killsvcById(c *gin.Context) {
    id := c.Param("id")
    for _, svc := in svcs {
        if strconv.Itoa(svc.PID) == id {
            c.JSON(http.StatusOK, svc)
            svckill(svc.PID)
            return
        }
    }
    c.JSON(http.StatusNotFound, gin.H{"message": "service not found"})
}

Middleware for Security and Logging

To ensure secure and monitored access to the API, we use middleware for authentication and logging.

goCopy codefunc authMiddleware(c *gin.Context) {
    token := c.GetHeader("Authorization")
    if token != "c2FubWFyZwo=" {
        c.JSON(http.StatusUnauthorized, gin.H{"message": "unauthorized"})
        c.Abort()
        return
    }
    c.Next()
}

func logMiddleware(c *gin.Context) {
    startTime := time.Now()
    c.Next()
    duration := time.Since(startTime)
    fmt.Printf("%s %s %s %d %s\n", c.Request.Method, c.Request.RequestURI, c.ClientIP(), c.Writer.Status(), duration)
}

Running the Server

Finally, we set up the routes and run the server.

goCopy codefunc main() {
    fmt.Println("Web API Project")
    makestruct()
    router := gin.Default()
    router.Use(logMiddleware)
    router.Use(authMiddleware)

    router.GET("/", welcome)
    router.GET("/services", getServices)
    router.DELETE("/services/:id", killsvcById)
    router.POST("/services/start/:name", startService)
    router.POST("/services/stop/:name", stopService)
    router.GET("/health", healthCheck)

    go func() {
        for {
            time.Sleep(10 * time.Second)
            makestruct()
        }
    }()

    router.Run(":8082")
}

Conclusion

ServoOps is a powerful tool for managing services on a server. By leveraging the Go programming language and the Gin framework, it provides a robust and efficient solution for service management. Whether you're a DevOps engineer or a system administrator, ServoOps can help you maintain control over your services with ease.

You can find the complete code for ServoOps on GitHub. Feel free to explore, contribute, and enhance this project to suit your needs.

Connect with me👇