Skip to content

Commit

Permalink
feat: add code runner for configuration code execution (#803)
Browse files Browse the repository at this point in the history
  • Loading branch information
adohe committed Feb 21, 2024
1 parent caaa44b commit 29d5701
Show file tree
Hide file tree
Showing 8 changed files with 177 additions and 0 deletions.
13 changes: 13 additions & 0 deletions pkg/cmd/generate/run/fake/fake.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package fake

import "kusionstack.io/kusion/pkg/cmd/generate/run"

var _ run.CodeRunner = &KPMRunner{}

// KPMRunner is a fake code runner for testing purposes.
type KPMRunner struct{}

// Run does nothing.
func (r *KPMRunner) Run(workDir string, arguments map[string]string) ([]byte, error) {
return nil, nil
}
57 changes: 57 additions & 0 deletions pkg/cmd/generate/run/run.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package run

import (
kcl "kcl-lang.io/kcl-go"
kclpkg "kcl-lang.io/kcl-go/pkg/kcl"
"kcl-lang.io/kpm/pkg/api"
"kcl-lang.io/kpm/pkg/opt"
)

// CodeRunner compiles and runs the target DSL based configuration code
// and returns configuration data in plain format.
type CodeRunner interface {
Run(workingDir string, arguments map[string]string) ([]byte, error)
}

// KPMRunner should implement the CodeRunner interface.
var _ CodeRunner = &KPMRunner{}

// KPMRunner implements the CodeRunner interface.
type KPMRunner struct{}

// Run calls KPM api to compile and run KCL based configuration code.
func (r *KPMRunner) Run(workDir string, arguments map[string]string) ([]byte, error) {
optList := buildKCLOptions(workDir, arguments)
result, err := api.RunWithOpts(
opt.WithKclOption(*kclpkg.NewOption().Merge(optList...)),
opt.WithNoSumCheck(true),
opt.WithLogWriter(nil),
)
if err != nil {
return nil, err
}

return []byte(result.GetRawYamlResult()), nil
}

// buildKCLOptions returns list of KCL options.
func buildKCLOptions(workDir string, arguments map[string]string) []kcl.Option {
optList := make([]kcl.Option, 2)

// build arguments option
for k, v := range arguments {
argStr := k + "=" + v
withOpt := kcl.WithOptions(argStr)
optList = append(optList, withOpt)
}

// build workDir option
withOpt := kcl.WithWorkDir(workDir)
optList = append(optList, withOpt)

// eliminate null values in the result
withOpt = kcl.WithDisableNone(true)
optList = append(optList, withOpt)

return optList
}
23 changes: 23 additions & 0 deletions pkg/cmd/generate/run/run_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package run

import (
"os"
"path/filepath"
"testing"
)

func TestKPMRunnerRun(t *testing.T) {
currentPath, err := os.Getwd()
if err != nil {
t.Fatalf("Failed to get current directory: %v", err)
}
workDir := filepath.Join(currentPath, "testdata/prod")
codeRunner := &KPMRunner{}
value, err := codeRunner.Run(workDir, nil)
if err != nil {
t.Fatalf("Failed to run configuration code: %v", err)
}
if len(value) == 0 {
t.Fatalf("Unexpected value output")
}
}
55 changes: 55 additions & 0 deletions pkg/cmd/generate/run/testdata/base/base.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import catalog.models.schema.v1 as ac
import catalog.models.schema.v1.workload as wl
import catalog.models.schema.v1.workload.container as c
import catalog.models.schema.v1.workload.container.probe as p

# base.k declares reusable configurations for all stacks.
helloworld: ac.AppConfiguration {
workload: wl.Service {
containers: {
"nginx": c.Container {
image: "nginx:v1"
# Run the following command as defined
command: ["/bin/sh", "-c", "echo hi"]
# Extra arguments append to command defined above
args: ["/bin/sh", "-c", "echo hi"]
env: {
# An environment variable of name "env1" and value "VALUE" will be set
"env1": "VALUE"
# An environment variable of name "env2" and value of the key "key" in the
# secret named "sec-name" will be set.
"env2": "secret://sec-name/key"
}
# Run the command "/bin/sh -c echo hi", as defined above, in the directory "/tmp"
workingDir: "/tmp"
# Configure a HTTP readiness probe
readinessProbe: p.Probe {
probeHandler: p.Http {
url: "http://localhost:80"
}
initialDelaySeconds: 10
}
}
}
# Set the replicas
replicas: 2
}
}

sampleapp: ac.AppConfiguration {
workload: wl.Service {
containers: {
"nginx": c.Container {
image: "nginx:v1"
# Run the following command as defined
command: ["/bin/sh", "-c", "echo hi"]
# Extra arguments append to command defined above
args: ["/bin/sh", "-c", "echo hi"]
# Run the command "/bin/sh -c echo hi", as defined above, in the directory "/tmp"
workingDir: "/tmp"
}
}
# Set the replicas
replicas: 2
}
}
9 changes: 9 additions & 0 deletions pkg/cmd/generate/run/testdata/prod/kcl.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "testdata"
version = "0.1.0"

[dependencies]
catalog = { git = "https://github.com/KusionStack/catalog.git", tag = "0.1.2" }
[profile]
entries = ["../base/base.k", "main.k"]

16 changes: 16 additions & 0 deletions pkg/cmd/generate/run/testdata/prod/main.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import catalog.models.schema.v1 as ac

# main.k declares customized configurations for prod stack.
helloworld: ac.AppConfiguration {
workload.containers.nginx: {
# prod stack has different image
image = "nginx:v2"
}
}

sampleapp: ac.AppConfiguration {
workload.containers.nginx: {
# prod stack has different image
image = "nginx:v3"
}
}
2 changes: 2 additions & 0 deletions pkg/cmd/generate/run/testdata/prod/stack.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# The stack basic info
name: prod
2 changes: 2 additions & 0 deletions pkg/cmd/generate/run/testdata/project.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# The project basic info
name: testdata

0 comments on commit 29d5701

Please sign in to comment.