Skip to content

Commit

Permalink
Implement CrcApiServer, which is the run as the crc daemon
Browse files Browse the repository at this point in the history
The CrcApiServer listens on a unix socket and handles requests of
the format {"command": "command_to_run, "args": "args_for_cmd"}
the args field in the json object is optional. It reads the request
from the connection and runs a handler function to serve the request
based on the command field. This commit also includes implementation
of a few of the handlers.

Fixes #796
  • Loading branch information
anjannath authored and praveenkumar committed Jan 25, 2020
1 parent c3cc33d commit c36a879
Show file tree
Hide file tree
Showing 5 changed files with 221 additions and 0 deletions.
33 changes: 33 additions & 0 deletions cmd/crc/cmd/daemon.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package cmd

import (
"github.com/code-ready/crc/pkg/crc/api"
"github.com/code-ready/crc/pkg/crc/constants"
"github.com/code-ready/crc/pkg/crc/logging"
"github.com/spf13/cobra"
"os"
)

func init() {
rootCmd.AddCommand(daemonCmd)
}

var daemonCmd = &cobra.Command{
Use: "daemon",
Short: "Run the crc daemon",
Long: "Run the crc daemon",
Hidden: true,
Run: func(cmd *cobra.Command, args []string) {
runDaemon()
},
}

func runDaemon() {
// Remove if an old socket is present
os.Remove(constants.DaemonSocketPath)
crcApiServer, err := api.CreateApiServer(constants.DaemonSocketPath)
if err != nil {
logging.Fatal("Failed to launch daemon", err)
}
crcApiServer.Serve()
}
93 changes: 93 additions & 0 deletions pkg/crc/api/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package api

import (
"bytes"
"encoding/json"
"fmt"
"net"

"github.com/code-ready/crc/pkg/crc/logging"
)

type ArgsType map[string]string
type handlerFunc func(ArgsType) string

type CrcApiServer struct {
listener net.Listener
handlers map[string]handlerFunc // relates commands to handler func
}

// commandRequest struct is used to decode the json request from tray
type commandRequest struct {
Command string `json:"command"`
Args map[string]string `json:"args,omitempty"`
}

func CreateApiServer(socketPath string) (CrcApiServer, error) {
listener, err := net.Listen("unix", socketPath)
if err != nil {
logging.Error("Failed to create socket: ", err.Error())
return CrcApiServer{}, err
}
apiServer := CrcApiServer{
listener: listener,
handlers: map[string]handlerFunc{
"start": startHandler,
"stop": stopHandler,
"status": statusHandler,
"delete": deleteHandler,
"version": versionHandler,
"webconsoleurl": webconsoleURLHandler,
},
}
return apiServer, nil
}

func (api CrcApiServer) Serve() {
for {
conn, err := api.listener.Accept()
if err != nil {
logging.Error("Error establishing communication: ", err.Error())
continue
}
go api.handleConnection(conn)
}
}

func (api CrcApiServer) handleConnection(conn net.Conn) {
defer conn.Close()
inBuffer := make([]byte, 1024)
var req commandRequest
numBytes, err := conn.Read(inBuffer)
if err != nil || numBytes == 0 || numBytes == cap(inBuffer) {
logging.Error("Error reading from socket")
return
}
logging.Debug("Received Request:", string(inBuffer[0:numBytes]))
err = json.Unmarshal(inBuffer[0:numBytes], &req)
if err != nil {
logging.Error("Error decoding request: ", err.Error())
return
}

if handler, ok := api.handlers[req.Command]; ok {
result := handler(req.Args)
writeStringToSocket(conn, result)
} else {
writeStringToSocket(conn, fmt.Sprintf("Unknown command supplied: %s", req.Command))
}
}

func writeStringToSocket(socket net.Conn, msg string) {
var outBuffer bytes.Buffer
_, err := outBuffer.WriteString(msg)
if err != nil {
logging.Error("Failed writing string to buffer", err.Error())
return
}
_, err = socket.Write(outBuffer.Bytes())
if err != nil {
logging.Error("Failed writing string to socket", err.Error())
return
}
}
87 changes: 87 additions & 0 deletions pkg/crc/api/handlers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package api

import (
"encoding/json"
"io/ioutil"

"github.com/code-ready/crc/cmd/crc/cmd/config"
crcConfig "github.com/code-ready/crc/pkg/crc/config"
"github.com/code-ready/crc/pkg/crc/constants"
"github.com/code-ready/crc/pkg/crc/logging"
"github.com/code-ready/crc/pkg/crc/machine"
"github.com/code-ready/crc/pkg/crc/validation"
"github.com/code-ready/crc/pkg/crc/version"
)

func statusHandler(_ ArgsType) string {
statusConfig := machine.ClusterStatusConfig{Name: constants.DefaultName}
clusterStatus, _ := machine.Status(statusConfig)
return encodeStructToJson(clusterStatus)
}

func stopHandler(_ ArgsType) string {
stopConfig := machine.StopConfig{
Name: constants.DefaultName,
Debug: true,
}
commandResult, _ := machine.Stop(stopConfig)
return encodeStructToJson(commandResult)
}

func startHandler(_ ArgsType) string {
startConfig := machine.StartConfig{
Name: constants.DefaultName,
BundlePath: crcConfig.GetString(config.Bundle.Name),
VMDriver: crcConfig.GetString(config.VMDriver.Name),
Memory: crcConfig.GetInt(config.Memory.Name),
CPUs: crcConfig.GetInt(config.CPUs.Name),
NameServer: crcConfig.GetString(config.NameServer.Name),
GetPullSecret: getPullSecretFileContent,
Debug: true,
}
status, _ := machine.Start(startConfig)
return encodeStructToJson(status)
}

func versionHandler(_ ArgsType) string {
v := &machine.VersionResult{
CrcVersion: version.GetCRCVersion(),
CommitSha: version.GetCommitSha(),
OpenshiftVersion: version.GetBundleVersion(),
Success: true,
}
return encodeStructToJson(v)
}

func getPullSecretFileContent() (string, error) {
data, err := ioutil.ReadFile(crcConfig.GetString(config.PullSecretFile.Name))
if err != nil {
return "", err
}
pullsecret := string(data)
if err := validation.ImagePullSecret(pullsecret); err != nil {
return "", err
}
return pullsecret, nil
}

func deleteHandler(_ ArgsType) string {
delConfig := machine.DeleteConfig{Name: constants.DefaultName}
r, _ := machine.Delete(delConfig)
return encodeStructToJson(r)
}

func webconsoleURLHandler(_ ArgsType) string {
consoleConfig := machine.ConsoleConfig{Name: constants.DefaultName}
r, _ := machine.GetConsoleURL(consoleConfig)
return encodeStructToJson(r)
}

func encodeStructToJson(v interface{}) string {
s, err := json.Marshal(v)
if err != nil {
logging.Error(err.Error())
return "Failed"
}
return string(s)
}
1 change: 1 addition & 0 deletions pkg/crc/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ var (
GlobalStatePath = filepath.Join(CrcBaseDir, GlobalStateFile)
DefaultBundlePath = filepath.Join(MachineCacheDir, GetDefaultBundle())
bundleEmbedded = "false"
DaemonSocketPath = filepath.Join(CrcBaseDir, "crc.sock")
)

// GetHomeDir returns the home directory for the current user
Expand Down
7 changes: 7 additions & 0 deletions pkg/crc/machine/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,10 @@ type ConsoleResult struct {
Success bool
Error string
}

type VersionResult struct {
CrcVersion string
CommitSha string
OpenshiftVersion string
Success bool
}

0 comments on commit c36a879

Please sign in to comment.