Skip to content

Commit

Permalink
feat: update workload generator support probe&lifecycle
Browse files Browse the repository at this point in the history
  • Loading branch information
adohe committed Aug 28, 2023
1 parent 5fb4e61 commit 5cc44b3
Show file tree
Hide file tree
Showing 7 changed files with 544 additions and 30 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ require (
github.com/variantdev/vals v0.21.0
github.com/zclconf/go-cty v1.12.1
go.uber.org/zap v1.24.0
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561
gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,7 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@ package workload

import (
"fmt"
"net/url"
"strings"

corev1 "k8s.io/api/core/v1"
"golang.org/x/exp/maps"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/util/intstr"

"kusionstack.io/kusion/pkg/generator/appconfiguration"
"kusionstack.io/kusion/pkg/models"
"kusionstack.io/kusion/pkg/models/appconfiguration/workload"
"kusionstack.io/kusion/pkg/models/appconfiguration/workload/container"
"kusionstack.io/kusion/pkg/projectstack"
"kusionstack.io/kusion/pkg/util/net"
)

type workloadGenerator struct {
Expand Down Expand Up @@ -71,29 +77,229 @@ func (g *workloadGenerator) Generate(spec *models.Spec) error {
return nil
}

func toOrderedContainers(appContainers map[string]container.Container) ([]corev1.Container, error) {
func toOrderedContainers(appContainers map[string]container.Container) ([]v1.Container, error) {
// Create a slice of containers based on the app's
// containers.
var containers []corev1.Container
var containers []v1.Container
if err := appconfiguration.ForeachOrdered(appContainers, func(containerName string, c container.Container) error {
// Create a slice of env vars based on the container's env vars.
var envs []corev1.EnvVar
var envs []v1.EnvVar
for k, v := range c.Env {
envs = append(envs, *MagicEnvVar(k, v))
}
resourceRequirements, err := handleResourceRequirementsV1(c.Resources)
if err != nil {
return err
}

// Create a container object and append it to the containers slice.
containers = append(containers, corev1.Container{
ctn := v1.Container{
Name: containerName,
Image: c.Image,
Command: c.Command,
Args: c.Args,
WorkingDir: c.WorkingDir,
Env: envs,
})
Resources: resourceRequirements,
}
err = updateContainer(&c, &ctn)
if err != nil {
return err
}
// Create a container object and append it to the containers slice.
containers = append(containers, ctn)
return nil
}); err != nil {
return nil, err
}
return containers, nil
}

// updateContainer updates v1.Container with passed parameters.
func updateContainer(in *container.Container, out *v1.Container) error {
if in.ReadinessProbe != nil {
readinessProbe, err := convertKusionProbeToV1Probe(in.ReadinessProbe)
if err != nil {
return err
}
out.ReadinessProbe = readinessProbe
}

if in.LivenessProbe != nil {
livenessProbe, err := convertKusionProbeToV1Probe(in.LivenessProbe)
if err != nil {
return err
}
out.LivenessProbe = livenessProbe
}

if in.StartupProbe != nil {
startupProbe, err := convertKusionProbeToV1Probe(in.StartupProbe)
if err != nil {
return err
}
out.StartupProbe = startupProbe
}

if in.Lifecycle != nil {
lifecycle, err := convertKusionLifecycleToV1Lifecycle(in.Lifecycle)
if err != nil {
return err
}
out.Lifecycle = lifecycle
}

return nil
}

// handleResourceRequirementsV1 parses the resources parameter if specified and
// returns ResourceRequirements.
func handleResourceRequirementsV1(resources map[string]string) (v1.ResourceRequirements, error) {
result := v1.ResourceRequirements{}
if resources == nil {
return result, nil
}
for key, value := range resources {
resourceName := v1.ResourceName(key)
requests, limits, err := populateResourceLists(resourceName, value)
if err != nil {
return result, err
}
if requests != nil && result.Requests == nil {
result.Requests = make(v1.ResourceList)
}
maps.Copy(result.Requests, requests)
if limits != nil && result.Limits == nil {
result.Limits = make(v1.ResourceList)
}
maps.Copy(result.Limits, limits)
}
return result, nil
}

// populateResourceLists takes strings of form <resourceName>=[<minValue>-]<maxValue> and
// returns request&limit ResourceList.
func populateResourceLists(name v1.ResourceName, spec string) (v1.ResourceList, v1.ResourceList, error) {
requests := v1.ResourceList{}
limits := v1.ResourceList{}

parts := strings.Split(spec, "-")
if len(parts) == 1 {
resourceQuantity, err := resource.ParseQuantity(parts[0])
if err != nil {
return nil, nil, err
}
limits[name] = resourceQuantity
} else if len(parts) == 2 {
resourceQuantity, err := resource.ParseQuantity(parts[0])
if err != nil {
return nil, nil, err
}
requests[name] = resourceQuantity
resourceQuantity, err = resource.ParseQuantity(parts[1])
if err != nil {
return nil, nil, err
}
limits[name] = resourceQuantity
}

return requests, limits, nil
}

// convertKusionProbeToV1Probe converts Kusion Probe to Kubernetes Probe types.
func convertKusionProbeToV1Probe(p *container.Probe) (*v1.Probe, error) {
result := &v1.Probe{
InitialDelaySeconds: p.InitialDelaySeconds,
TimeoutSeconds: p.TimeoutSeconds,
PeriodSeconds: p.PeriodSeconds,
SuccessThreshold: p.SuccessThreshold,
FailureThreshold: p.FailureThreshold,
}
probeHandler := p.ProbeHandler
switch probeHandler.Type {
case "Http":
action, err := httpGetAction(probeHandler.HTTPGetAction.URL, probeHandler.Headers)
if err != nil {
return nil, err
}
result.HTTPGet = action
case "Exec":
result.Exec = &v1.ExecAction{Command: probeHandler.Command}
case "Tcp":
action, err := tcpSocketAction(probeHandler.TCPSocketAction.URL)
if err != nil {
return nil, err
}
result.TCPSocket = action
}
return result, nil
}

// convertKusionLifecycleToV1Lifecycle converts Kusion Lifecycle to Kubernetes Lifecycle types.
func convertKusionLifecycleToV1Lifecycle(l *container.Lifecycle) (*v1.Lifecycle, error) {
result := &v1.Lifecycle{}
if l.PreStop != nil {
preStop, err := lifecycleHandler(l.PreStop)
if err != nil {
return nil, err
}
result.PreStop = preStop
}
if l.PostStart != nil {
postStart, err := lifecycleHandler(l.PostStart)
if err != nil {
return nil, err
}
result.PostStart = postStart
}
return result, nil
}

func lifecycleHandler(in *container.LifecycleHandler) (*v1.LifecycleHandler, error) {
result := &v1.LifecycleHandler{}
switch in.Type {
case "Http":
action, err := httpGetAction(in.HTTPGetAction.URL, in.Headers)
if err != nil {
return nil, err
}
result.HTTPGet = action
case "Exec":
result.Exec = &v1.ExecAction{Command: in.Command}
}
return result, nil
}

func httpGetAction(urlstr string, headers map[string]string) (*v1.HTTPGetAction, error) {
u, err := url.Parse(urlstr)
if err != nil {
return nil, err
}

httpHeaders := make([]v1.HTTPHeader, 0, len(headers))
for k, v := range headers {
httpHeaders = append(httpHeaders, v1.HTTPHeader{
Name: k,
Value: v,
})
}

return &v1.HTTPGetAction{
Path: u.Path,
Port: intstr.FromString(u.Port()),
Host: u.Hostname(),
Scheme: v1.URIScheme(strings.ToUpper(u.Scheme)),
HTTPHeaders: httpHeaders,
}, nil
}

func tcpSocketAction(urlstr string) (*v1.TCPSocketAction, error) {
host, port, err := net.ParseHostPort(urlstr)
if err != nil {
return nil, err
}

return &v1.TCPSocketAction{
Port: intstr.FromString(port),
Host: host,
}, nil
}
Loading

0 comments on commit 5cc44b3

Please sign in to comment.