Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .devcontainer/dev.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
FROM docker:26-cli AS dockercli

FROM golang:1.23-bullseye

WORKDIR /app
Expand All @@ -19,13 +21,15 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
pkg-config \
&& rm -rf /var/lib/apt/lists/*

COPY --from=dockercli /usr/local/bin/docker /usr/local/bin/docker

COPY go.mod go.sum ./
RUN go mod download

RUN go install golang.org/x/tools/gopls@v0.16.2 && \
go install honnef.co/go/tools/cmd/staticcheck@v0.5.0

RUN CGO_ENABLED=1 go install -ldflags "-s -w -extldflags '-static'" github.com/go-delve/delve/cmd/dlv@latest
RUN go install github.com/go-delve/delve/cmd/dlv@v1.23.1

RUN mkdir -p storage && chmod 777 storage

Expand Down
12 changes: 9 additions & 3 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,26 @@
},
"workspaceFolder": "/app",
"workspaceMount": "source=${localWorkspaceFolder},target=/app,type=bind",
"appPort": ["8080:8080"],
"appPort": [
"8080:8080"
],
"postCreateCommand": "go mod tidy",
"customizations": {
"vscode": {
"settings": {
"terminal.integrated.defaultProfile.linux": "sh"
},
"extensions": [
"golang.go",
"golang.go",
"ms-vscode-remote.remote-containers",
"ms-azuretools.vscode-docker"
]
}
},
"remoteUser": "root",
"runArgs": ["--privileged"]
"runArgs": [
"--privileged",
"-v",
"/var/run/docker.sock:/var/run/docker.sock"
]
}
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"request": "launch",
"mode": "debug",
"program": "cmd/server/main.go",
"buildFlags": ["-gcflags=all=-N -l"]
"buildFlags": ["-gcflags=all=-N"]
},
{
"name": "Launch AkoFlow Client Run",
Expand Down
14 changes: 9 additions & 5 deletions pkg/server/config/app_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import (
"github.com/ovvesley/akoflow/pkg/server/config/http_helper"
"github.com/ovvesley/akoflow/pkg/server/config/http_render_view"
"github.com/ovvesley/akoflow/pkg/server/config/logger"
"github.com/ovvesley/akoflow/pkg/server/connector/connector_hpc"
"github.com/ovvesley/akoflow/pkg/server/connector/connector_k8s"
"github.com/ovvesley/akoflow/pkg/server/connector/connector_sdumont"
"github.com/ovvesley/akoflow/pkg/server/connector/connector_local"
"github.com/ovvesley/akoflow/pkg/server/connector/connector_singularity"
"github.com/ovvesley/akoflow/pkg/server/database/repository/activity_repository"
"github.com/ovvesley/akoflow/pkg/server/database/repository/logs_repository"
Expand Down Expand Up @@ -56,7 +57,8 @@ type AppContainerRepository struct {
type AppContainerConnector struct {
K8sConnector connector_k8s.IConnector
SingularityConnector connector_singularity.IConnectorSingularity
SDumontConnector connector_sdumont.IConnectorSDumont
HPCRuntimeConnector connector_hpc.IConnectorHPCRuntime
LocalConnector connector_local.IConnectorLocal
}

type AppContainerTemplateRenderer struct {
Expand All @@ -74,7 +76,7 @@ func GetEnvVars() (map[string]string, map[string]map[string]string) {
envVars := make(map[string]string)
envVarByRuntime := make(map[string]map[string]string)

runtimes_avaibles := []string{"k8s", "singularity", "sdumont"}
runtimes_avaibles := []string{"k8s", "singularity", "hpc"}

for _, v := range os.Environ() {
splitted := strings.Split(v, "=")
Expand Down Expand Up @@ -120,7 +122,8 @@ func MakeAppContainer() AppContainer {
// create the Connector instances
k8sConnector := connector_k8s.New()
singularityConnector := connector_singularity.New()
sdumontConnector := connector_sdumont.New()
hpcConnector := connector_hpc.New()
localConnector := connector_local.New()

renderViewprovider := http_render_view.New()

Expand All @@ -145,7 +148,8 @@ func MakeAppContainer() AppContainer {
Connector: AppContainerConnector{
K8sConnector: k8sConnector,
SingularityConnector: singularityConnector,
SDumontConnector: sdumontConnector,
HPCRuntimeConnector: hpcConnector,
LocalConnector: localConnector,
},
TemplateRenderer: AppContainerTemplateRenderer{
RenderViewProvider: renderViewprovider,
Expand Down
10 changes: 9 additions & 1 deletion pkg/server/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,16 @@ func loadDotEnv() {
if line != "" {
env := strings.Split(line, "=")
key := strings.TrimSpace(env[0])
value := strings.Trim(strings.TrimSpace(env[1]), "'")
value := ""

if len(env) == 2 {
value = strings.TrimSpace(env[1])
} else if len(env) > 2 {
value = strings.TrimSpace(strings.Join(env[1:], "="))
}
value = strings.TrimSpace(value)
os.Setenv(key, value)

}
}

Expand Down
243 changes: 243 additions & 0 deletions pkg/server/connector/connector_hpc/connector_hpc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
package connector_hpc

import (
"encoding/base64"
"fmt"
"os/exec"
"sync"
"syscall"

"github.com/ovvesley/akoflow/pkg/server/entities/runtime_entity"
)

type ConnectorHPCRuntime struct {
Runtime runtime_entity.Runtime
}

func New() IConnectorHPCRuntime {
return &ConnectorHPCRuntime{}
}

func (c *ConnectorHPCRuntime) SetRuntime(runtime runtime_entity.Runtime) *ConnectorHPCRuntime {
c.Runtime = runtime
return c
}

type IConnectorHPCRuntime interface {
RunCommand(command string, args ...string) (string, error)
RunCommandWithOutput(command string, args ...string) (string, error)
RunCommandWithOutputRemote(command string, args ...string) (string, error)
IsVPNConnected() (bool, error)
ExecuteMultiplesCommand(commands []string)
SetRuntime(runtime runtime_entity.Runtime) *ConnectorHPCRuntime
BuildRemoteCommand(runtime runtime_entity.Runtime, command string) (string, error)
}

func (c *ConnectorHPCRuntime) RunCommandWithOutputRemote(command string, args ...string) (string, error) {
fmt.Printf("Executing command: %s %v\n", command, args)

shell := getAvailableShell()

remoteCommand, err := c.BuildRemoteCommand(c.Runtime, command)
if err != nil {
return "", err
}

fullCommand := append([]string{"-c", remoteCommand}, args...)
cmd := exec.Command(shell, fullCommand...)
output, err := cmd.CombinedOutput()

println(string(output))
if err != nil {
return "", fmt.Errorf("failed to execute command: %w", err)
}

return string(output), nil
}

func (c *ConnectorHPCRuntime) handleCreateSSHKey(privateKey string, publicKey string, sshConfig string) error {
if privateKey == "" || publicKey == "" || sshConfig == "" {
return nil
}

privateKeyDecoded, err := decodeBase64(privateKey)
if err != nil {
return err
}

publicKeyDecoded, err := decodeBase64(publicKey)
if err != nil {
return err
}

sshConfigDecoded, err := decodeBase64(sshConfig)
if err != nil {
return err
}

privateKeyFile, err := writeTempSSHKey(privateKeyDecoded, "id_rsa")
if err != nil {
return err
}

publicKeyFile, err := writeTempSSHKey(publicKeyDecoded, "id_rsa.pub")
if err != nil {
removeTempSSHKey(privateKeyFile)
return err
}

_, err = writeTempSSHKey(sshConfigDecoded, "config")

if err != nil {
removeTempSSHKey(privateKeyFile)
removeTempSSHKey(publicKeyFile)
return err
}

return nil
}

func (c ConnectorHPCRuntime) BuildRemoteCommand(runtime runtime_entity.Runtime, command string) (string, error) {
username := runtime.GetCurrentRuntimeMetadata("USER")
hostname := runtime.GetCurrentRuntimeMetadata("HOST_CLUSTER")
sshKeyPrivateKey := runtime.GetCurrentRuntimeMetadata("SSHKEYPRIVK")
sshKeyPublicKey := runtime.GetCurrentRuntimeMetadata("SSHKEYPUBLK")
sshConfig := runtime.GetCurrentRuntimeMetadata("SSHCONFIG")
password := runtime.GetCurrentRuntimeMetadata("PASSWORD")

// create .ssh/id_rsa file with the private key
c.handleCreateSSHKey(sshKeyPrivateKey, sshKeyPublicKey, sshConfig)

if sshKeyPrivateKey != "" && sshKeyPublicKey != "" && sshConfig != "" {
return fmt.Sprintf("ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -o ConnectTimeout=10 %s@%s '%s'", username, hostname, command), nil
} else if password != "" {
return fmt.Sprintf("sshpass -p '%s' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -o ConnectTimeout=10 %s@%s '%s'", password, username, hostname, command), nil
}

return "", fmt.Errorf("no authentication method provided")
}

func decodeBase64(encoded string) (string, error) {
decoded, err := base64.StdEncoding.DecodeString(encoded)
if err != nil {
return "", fmt.Errorf("failed to decode base64: %w", err)
}
return string(decoded), nil
}

func writeTempSSHKey(key string, filename string) (string, error) {
path := "/root/.ssh/"

keyFile := path + filename

_, err := exec.Command("bash", "-c", fmt.Sprintf("test -f %s", keyFile)).Output()
if err == nil {
return keyFile, nil
}

err = exec.Command("bash", "-c", fmt.Sprintf("echo '%s' > %s && chmod 600 %s", key, keyFile, keyFile)).Run()
if err != nil {
return "", fmt.Errorf("failed to write SSH key to file: %w", err)
}

return keyFile, nil
}

func removeTempSSHKey(file string) {
exec.Command("bash", "-c", fmt.Sprintf("rm -f %s", file)).Run()
}

func (s *ConnectorHPCRuntime) ExecuteMultiplesCommand(commands []string) {
var wg sync.WaitGroup

responses := make(chan string, len(commands)) // Create a channel to receive the responses

for _, command := range commands {
wg.Add(1)
go func(command string) {
defer wg.Done()

shell := getAvailableShell()

fullCommand := append([]string{"-c", command})
cmd := exec.Command(shell, fullCommand...)
output, err := cmd.CombinedOutput()

fmt.Printf("Executing command: %s %v\n", command, fullCommand)

if err != nil {
fmt.Printf("failed to execute command: %s\n", err)
}

fmt.Printf("Output: %s\n", output)

responses <- string(output)

}(command)
}

wg.Wait()

close(responses)

for response := range responses {
fmt.Printf("Response: %s\n", response)
}
}

func (c *ConnectorHPCRuntime) RunCommand(command string, args ...string) (string, error) {
return executeCommand(command, args...)
}

func (c *ConnectorHPCRuntime) RunCommandWithOutput(command string, args ...string) (string, error) {
fmt.Printf("Executing command: %s %v\n", command, args)

shell := getAvailableShell()

fullCommand := append([]string{"-c", command}, args...)
cmd := exec.Command(shell, fullCommand...)
output, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("failed to execute command: %w", err)
}

return string(output), nil

}

func (c *ConnectorHPCRuntime) IsVPNConnected() (bool, error) {
output, err := c.RunCommandWithOutput("ps aux | grep vpnc")
if err != nil {
return false, err
}

if len(output) > 0 {
return true, nil
}

return false, nil
}

func executeCommand(command string, args ...string) (string, error) {
fmt.Printf("Executing command: %s %v\n", command, args)

shell := getAvailableShell()
fullCommand := append([]string{"-c", command}, args...)
cmd := exec.Command(shell, fullCommand...)
cmd.SysProcAttr = &syscall.SysProcAttr{}
if err := cmd.Start(); err != nil {
return "", fmt.Errorf("failed to start command: %w", err)
}

pid := cmd.Process.Pid
fmt.Printf("Started process with PID: %d\n", pid)

return fmt.Sprintf("%d", pid), nil
}

func getAvailableShell() string {
if _, err := exec.LookPath("bash"); err == nil {
return "bash"
}
return "sh"
}
Loading