Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
apploader/conf/local.yml
43 changes: 43 additions & 0 deletions apploader/internal/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package config

import "os"

// Config holds application configuration
type Config struct {
Server ServerConfig
Cvm CvmConfig
}

// ServerConfig holds server configuration
type ServerConfig struct {
Port string
}

// CvmConfig holds cvm configuration
type CvmConfig struct {
ConfigPath string
SupervisorPath string
SupervisorTemplatePath string
}

// Load loads configuration from environment variables
func Load() *Config {
return &Config{
Server: ServerConfig{
Port: getEnv("PORT", ":9090"),
},
Cvm: CvmConfig{
ConfigPath: getEnv("CVM_CONFIG_PATH", "/workplace/cvm-app/config/app.yml"),
SupervisorPath: getEnv("SUPERVISOR_PATH", "/workplace/supervisord/apploader"),
SupervisorTemplatePath: getEnv("SUPERVISOR_TEMPLATE_PATH", "conf/supervisord.ini.template"),
},
}
}

// getEnv gets an environment variable with a fallback default value
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
206 changes: 62 additions & 144 deletions apploader/internal/cvm/cvm.go
Original file line number Diff line number Diff line change
@@ -1,153 +1,94 @@
package cvm

/*
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
*/
import "C"
import (
"apploader/internal/config"
"apploader/internal/secret"
"apploader/pkg/command"
"apploader/pkg/conversion"
"apploader/pkg/file"
"fmt"
"log"
"os"
"os/exec"
"path"
"strings"
"syscall"
"text/template"
"time"
"unsafe"

"gopkg.in/yaml.v3"
)

const (
JOB = "job"
SERVER = "server"
DockerApp = "dockerApp"
APP = "app"
JOB = "job"
SERVER = "server"
)

const (
SUPERVISOR_PATH = "/workplace/supervisord/apploader"
)

func DoJob(ca *TaskInfo) error {
if ca.Type != JOB {
return fmt.Errorf("this task is not a job")
}

envs := make([]string, 0)
for k, v := range secret.Secret {
envs = append(envs, fmt.Sprintf("%s=%s", k, v))
}

if ca.Env != nil {
userEnv, ok := ca.Env.(map[string]interface{})
if !ok {
return fmt.Errorf("user env format error")
}

for k, v := range userEnv {
envs = append(envs, fmt.Sprintf("%s=%s", k, v))
}
}

log.Printf("entrypoint is %s", ca.Entrypoint)
return RunCommand(ca.Entrypoint, envs, ca.Args...)

type CvmBootManager interface {
Start()
}

func RunCommand(name string, envs []string, arg ...string) error {
cmd := exec.Command(name, arg...)
type cvmBootManager struct {
config *config.CvmConfig
cvmBootSequence *CvmBootSequence
}

cmd.Dir = path.Dir(name)
//
stdout, err := cmd.StdoutPipe()
cmd.Stderr = cmd.Stdout
cmd.Env = envs
// NewCvmBootManager creates a new cvm service
func NewCvmBootManager(config *config.CvmConfig) (CvmBootManager, error) {
service := &cvmBootManager{config: config}
cvmBootSequence, err := service.loadConfig()
if err != nil {
return err
}
if err = cmd.Start(); err != nil {
return err
}
//
for {
tmp := make([]byte, 128)
_, err := stdout.Read(tmp)
fmt.Print(string(tmp))
if err != nil {
break
}
return nil, err
}
if err = cmd.Wait(); err != nil {
if ex, ok := err.(*exec.ExitError); ok {
cmdExitStatus := ex.Sys().(syscall.WaitStatus).ExitStatus()
log.Println(cmdExitStatus)
}
log.Println(err)
return err
}
return nil
service.cvmBootSequence = cvmBootSequence
return service, nil
}

func Execv(main string, args ...string) {
workdir := path.Dir(main)
if err := os.Chdir(workdir); err != nil {
log.Fatalf("change to work dir failed, error: %s\n", err.Error())
}
// Start starts the cvm service
func (s *cvmBootManager) Start() {
s.startTask(s.cvmBootSequence.CvmAssistants)
s.startTask(s.cvmBootSequence.AppInfo)
}

sizeOfArgs_C := len(args) + 2 // +2 for [0]=main [1:end]=args [end]=nil
Args_C := make([]*C.char, sizeOfArgs_C)
Args_C[0] = C.CString(main)
defer C.free(unsafe.Pointer(Args_C[0]))
for i, arg := range args {
Args_C[i+1] = C.CString(arg)
defer C.free(unsafe.Pointer(Args_C[i+1]))
// loadConfig loads the cvm app config
func (cbm *cvmBootManager) loadConfig() (*CvmBootSequence, error) {
appfile, err := os.ReadFile(cbm.config.ConfigPath)
if err != nil {
return nil, fmt.Errorf("read %s failed, error: %s", cbm.config.ConfigPath, err.Error())
}
Args_C[sizeOfArgs_C-1] = nil
re := C.execv(C.CString(main), (**C.char)(unsafe.Pointer(&Args_C[0])))
if re != 0 {
log.Fatalf("execv %s failed, code is %d\n", main, re)
cvmBootSequence := new(CvmBootSequence)
err = yaml.Unmarshal(appfile, &cvmBootSequence)
if err != nil {
return nil, fmt.Errorf("unmarshal %s failed, error: %s", cbm.config.ConfigPath, err.Error())
}
return
return cvmBootSequence, nil
}

func ExecvDockerApp(ca *TaskInfo) {
if ca.Type != DockerApp {
log.Fatalf("task is not a docker app")
func (cbm *cvmBootManager) DoJob(taskInfo *TaskInfo) error {
if taskInfo.Type != JOB {
return fmt.Errorf("this task is not a job")
}

envs := make([]string, 0)
for k, v := range secret.Secret {
err := os.Setenv(k, v)
if err != nil {
log.Fatalf("set secret env failed, error: %s\n", err.Error())
}
envs = append(envs, fmt.Sprintf("%s=%s", k, v))
}

if ca.Env != nil {
userEnv, ok := ca.Env.(map[string]interface{})
if taskInfo.Env != nil {
userEnv, ok := taskInfo.Env.(map[string]interface{})
if !ok {
log.Fatalf("user env format error")
return fmt.Errorf("user env format error")
}

for k, v := range userEnv {
err := os.Setenv(k, v.(string))
if err != nil {
log.Fatalf("set app env failed, error: %s\n", err.Error())
}
envs = append(envs, fmt.Sprintf("%s=%s", k, v))
}
}

Execv(ca.Entrypoint, ca.Args...)
log.Printf("entrypoint is %s", taskInfo.Entrypoint)
return command.RunCommand(taskInfo.Entrypoint, envs, taskInfo.Args...)

}

func CreateSevers(ca *TaskInfo) error {
if ca.Type != SERVER {
func (cbm *cvmBootManager) CreateSevers(taskInfo *TaskInfo) error {
if taskInfo.Type != SERVER {
return fmt.Errorf("task is not a server")
}

Expand All @@ -158,8 +99,8 @@ func CreateSevers(ca *TaskInfo) error {
// envs = append(envs, fmt.Sprintf("%s=%s", k, v))
//}

if ca.Env != nil {
userEnv, ok := ca.Env.(map[string]interface{})
if taskInfo.Env != nil {
userEnv, ok := taskInfo.Env.(map[string]interface{})
if !ok {
return fmt.Errorf("user env format error")
}
Expand All @@ -170,84 +111,61 @@ func CreateSevers(ca *TaskInfo) error {
}

sConf := new(SupervisorConf)
sConf.Name = ca.Name
sConf.Command = ca.Entrypoint + " " + strings.Join(ca.Args, " ")
sConf.Workplace = path.Dir(ca.Entrypoint)
sConf.Name = taskInfo.Name
sConf.Command = taskInfo.Entrypoint + " " + strings.Join(taskInfo.Args, " ")
sConf.Workplace = path.Dir(taskInfo.Entrypoint)
sConf.Environment = strings.Join(envs, ",")
sConf.Priority = ca.Priority
sConf.Priority = taskInfo.Priority

tmpl, err := template.ParseFiles("conf/supervisord.ini.template")
tmpl, err := template.ParseFiles(cbm.config.SupervisorTemplatePath)
if err != nil {
return fmt.Errorf("parse supervisord template file failed, error: %s\n", err.Error())
return fmt.Errorf("parse %s failed, error: %s", cbm.config.SupervisorTemplatePath, err.Error())
}

supervisordINIPath := path.Join(SUPERVISOR_PATH, fmt.Sprintf("%s.ini", ca.Name))
supervisordINIPath := path.Join(cbm.config.SupervisorPath, fmt.Sprintf("%s.ini", taskInfo.Name))
if file.IsFile(supervisordINIPath) {
os.RemoveAll(supervisordINIPath)
}

f, err := os.Create(supervisordINIPath)
if err != nil {
return fmt.Errorf("create supervisord.ini failed, error: %s\n", err.Error())
return fmt.Errorf("create %s failed, error: %s", supervisordINIPath, err.Error())
}
defer f.Close()

if err := tmpl.Execute(f, sConf); err != nil {
return fmt.Errorf("file the supervisord.ini failed, error: %s\n", err.Error())
return fmt.Errorf("fill the %s failed, error: %s", supervisordINIPath, err.Error())
}

return nil
}

func Start() {
appfile, err := os.ReadFile("conf/app.yml")
if err != nil {
log.Fatalf("read app.yml failed, error: %s\n", err.Error())
}
cvmApp := new(CvmApp)
err = yaml.Unmarshal(appfile, &cvmApp)
if err != nil {
log.Fatalf("unmarshal app.yml failed, error: %s\n", err.Error())
}
time.Sleep(5 * time.Second)

log.Println("do all the job over")

startTask(cvmApp.CvmAssistants)

startTask(cvmApp.AppInfo)
}

func startTask(tasks []*TaskInfo) {
func (cbm *cvmBootManager) startTask(tasks []*TaskInfo) {
for i, t := range tasks {
switch t.Type {
case JOB:
log.Printf("begin to do job %s\n", t.Name)
err := DoJob(t)
err := cbm.DoJob(t)
if err != nil {
log.Fatalf("do job %s failed, error: %s\n", t.Name, err.Error())
}
log.Printf("end to do job %s\n", t.Name)
case SERVER:
log.Printf("begin to deploy server %s\n", t.Name)
t.Priority = i + 2
err := CreateSevers(t)
err := cbm.CreateSevers(t)
if err != nil {
log.Fatalf("deploy server %s failed, error: %s\n", t.Name, err)
}
err = RunCommand("supervisorctl", nil, "update")
err = command.RunCommand("supervisorctl", nil, "update")
if err != nil {
log.Fatalf("update supervisor conf failed, error: %s", err.Error())
}
err = RunCommand("supervisorctl", nil, "start", t.Name)
err = command.RunCommand("supervisorctl", nil, "start", t.Name)
if err != nil {
log.Fatalf("start %s failed, error: %s", t.Name, err.Error())
}
log.Printf("end to deply server %s\n", t.Name)
case DockerApp:
log.Printf("begin to run docker app %s\n", t.Name)
log.Printf("docker app will only run the first app\n")
ExecvDockerApp(t)
default:
log.Fatalf("task type: %s does not support", t.Type)
}
Expand Down
4 changes: 2 additions & 2 deletions apploader/internal/cvm/types.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package cvm

// CvmApp is the main application configuration
type CvmApp struct {
// CvmBootSequence is the main application configuration
type CvmBootSequence struct {
Kind string `yaml:"kind"`
AppInfo []*TaskInfo `yaml:"app"`
CvmAssistants []*TaskInfo `yaml:"csvAssistants"`
Expand Down
9 changes: 9 additions & 0 deletions apploader/main.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
package main

import (
"apploader/internal/config"
"apploader/internal/cvm"
"apploader/internal/secret"
"log"
"os"
"time"
)

func main() {
log.SetFlags(log.Lshortfile | log.LstdFlags)

cfg := config.Load()
cvm, err := cvm.NewCvmBootManager(&cfg.Cvm)
if err != nil {
log.Fatalf("failed to create cvm boot manager: %v", err)
}
go secret.StartSecretServer()
time.Sleep(2 * time.Second) // wait for secret server to start
cvm.Start()
os.Exit(0)
}
Loading