Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
wonderflow committed Feb 2, 2021
1 parent 70d04cd commit dc21fd1
Show file tree
Hide file tree
Showing 9 changed files with 239 additions and 101 deletions.
21 changes: 21 additions & 0 deletions apis/core.oam.dev/v1alpha2/application_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,27 @@ type AppStatus struct {

// Components record the related Components created by Application Controller
Components []runtimev1alpha1.TypedReference `json:"components,omitempty"`

// Services record the status of the application services
Services []ApplicationComponentStatus `json:"services,omitempty"`
}

// ApplicationComponentStatus record the health status of App component
type ApplicationComponentStatus struct {
Name string `json:"name"`
Healthy bool `json:"healthy"`
Message string `json:"message,omitempty"`
Traits []ApplicationTraitStatus `json:"traits,omitempty"`
}

// ApplicationTraitStatus records the trait health status
type ApplicationTraitStatus struct {
// Type is the trait type, a type can contains multiple `outputs` with different name
Type string `json:"type"`
// Name can be empty it is generated from `output`
Name string `json:"name,omitempty"`
Healthy bool `json:"healthy"`
Message string `json:"message,omitempty"`
}

// ApplicationTrait defines the trait of application
Expand Down
68 changes: 45 additions & 23 deletions pkg/appfile/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const (
// AppfileBuiltinConfig defines the built-in config variable
AppfileBuiltinConfig = "config"

// OAMApplicationLabel is application's metadata label
// OAMApplicationLabel is application's metadata label tagged on AC and Component
OAMApplicationLabel = "application.oam.dev"
)

Expand All @@ -32,10 +32,12 @@ type Workload struct {
Type string
CapabilityCategory types.CapabilityCategory
Params map[string]interface{}
Template string
Health string
Traits []*Trait
Scopes []Scope

Template string
HealthCheckPolicy string
CustomStatusFormat string
}

// GetUserConfigName get user config from AppFile, it will contain config file in it.
Expand All @@ -59,9 +61,18 @@ func (wl *Workload) EvalContext(ctx process.Context) error {
return definition.NewWDTemplater(wl.Name, wl.Template, "").Params(wl.Params).Complete(ctx)
}

// EvalStatus eval workload status
func (wl *Workload) EvalStatus(ctx process.Context, cli client.Client, ns string) (string, error) {
return definition.NewTDTemplater(wl.Name, "", "").Status(ctx, cli, ns, wl.CustomStatusFormat)
}

// EvalHealth eval workload health check
func (wl *Workload) EvalHealth(ctx process.Context, client client.Client, name string) error {
return definition.NewWDTemplater(wl.Name, "", wl.Health).Output(ctx, client, name).HealthCheck()
func (wl *Workload) EvalHealth(ctx process.Context, client client.Client, name string) (bool, error) {
temp := definition.NewWDTemplater(wl.Name, "", wl.HealthCheckPolicy)
if err := temp.GetK8sResource(ctx, client, name); err != nil {
return false, err
}
return temp.HealthCheck()
}

// Scope defines the scope of workload
Expand All @@ -75,9 +86,10 @@ type Trait struct {
Name string
CapabilityCategory types.CapabilityCategory
Params map[string]interface{}

Template string
Health string
Status string
HealthCheckPolicy string
CustomStatusFormat string
}

// EvalContext eval trait template and set result to context
Expand All @@ -87,12 +99,16 @@ func (trait *Trait) EvalContext(ctx process.Context) error {

// EvalStatus eval trait status
func (trait *Trait) EvalStatus(ctx process.Context, cli client.Client, ns string) (string, error) {
return definition.NewTDTemplater(trait.Name, "", "").Status(ctx, cli, ns, trait.Status)
return definition.NewTDTemplater(trait.Name, "", "").Status(ctx, cli, ns, trait.CustomStatusFormat)
}

// EvalHealth eval trait health check
func (trait *Trait) EvalHealth(ctx process.Context, client client.Client, name string) error {
return definition.NewTDTemplater(trait.Name, "", trait.Health).Output(ctx, client, name).HealthCheck()
func (trait *Trait) EvalHealth(ctx process.Context, client client.Client, name string) (bool, error) {
temp := definition.NewTDTemplater(trait.Name, "", trait.HealthCheckPolicy)
if err := temp.GetK8sResource(ctx, client, name); err != nil {
return false, err
}
return temp.HealthCheck()
}

// Appfile describes application
Expand Down Expand Up @@ -148,7 +164,8 @@ func (p *Parser) parseWorkload(comp v1alpha2.ApplicationComponent) (*Workload, e
}
workload.CapabilityCategory = templ.CapabilityCategory
workload.Template = templ.TemplateStr
workload.Health = templ.Health
workload.HealthCheckPolicy = templ.Health
workload.CustomStatusFormat = templ.CustomStatus
settings, err := util.RawExtension2Map(&comp.Settings)
if err != nil {
return nil, errors.WithMessagef(err, "fail to parse settings for %s", comp.Name)
Expand Down Expand Up @@ -193,8 +210,8 @@ func (p *Parser) parseTrait(name string, properties map[string]interface{}) (*Tr
CapabilityCategory: templ.CapabilityCategory,
Params: properties,
Template: templ.TemplateStr,
Health: templ.Health,
Status: templ.CustomStatus,
HealthCheckPolicy: templ.Health,
CustomStatusFormat: templ.CustomStatus,
}, nil
}

Expand Down Expand Up @@ -223,7 +240,7 @@ func (p *Parser) GenerateApplicationConfiguration(app *Appfile, ns string) (*v1a
return nil, nil, err
}
}
comp, acComp, err := evalWorkloadWithContext(pCtx, wl)
comp, acComp, err := evalWorkloadWithContext(pCtx, wl, app.Name, wl.Name)
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -283,20 +300,19 @@ func PrintApplicationComponents(app *Appfile, cli client.Client, ns string,
}

// evalWorkloadWithContext evaluate the workload's template to generate component and ACComponent
func evalWorkloadWithContext(pCtx process.Context, wl *Workload) (*v1alpha2.Component, *v1alpha2.ApplicationConfigurationComponent, error) {
func evalWorkloadWithContext(pCtx process.Context, wl *Workload, appName, compName string) (*v1alpha2.Component, *v1alpha2.ApplicationConfigurationComponent, error) {
base, assists := pCtx.Output()
componentWorkload, err := base.Unstructured()
if err != nil {
return nil, nil, err
}
workloadType := wl.Type
labels := componentWorkload.GetLabels()
if labels == nil {
labels = map[string]string{oam.WorkloadTypeLabel: workloadType}
} else {
labels[oam.WorkloadTypeLabel] = workloadType

labels := map[string]string{
oam.WorkloadTypeLabel: wl.Type,
oam.LabelAppName: appName,
oam.LabelAppComponent: compName,
}
componentWorkload.SetLabels(labels)
util.AddLabels(componentWorkload, labels)

component := &v1alpha2.Component{}
component.Spec.Workload.Object = componentWorkload
Expand All @@ -308,7 +324,13 @@ func evalWorkloadWithContext(pCtx process.Context, wl *Workload) (*v1alpha2.Comp
if err != nil {
return nil, nil, err
}
tr.SetLabels(map[string]string{oam.TraitTypeLabel: assist.Type})
labels := map[string]string{
oam.TraitTypeLabel: assist.Type,
oam.LabelAppName: appName,
oam.LabelAppComponent: compName,
oam.TraitResource: assist.Name,
}
util.AddLabels(tr, labels)
acComponent.Traits = append(acComponent.Traits, v1alpha2.ComponentTrait{
Trait: runtime.RawExtension{
Object: tr,
Expand Down
51 changes: 48 additions & 3 deletions pkg/commands/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"strings"
"time"

"github.com/oam-dev/kubevela/pkg/oam"

runtimev1alpha1 "github.com/crossplane/crossplane-runtime/apis/core/v1alpha1"
"github.com/fatih/color"
"github.com/pkg/errors"
Expand Down Expand Up @@ -192,9 +194,14 @@ func printComponentStatus(ctx context.Context, c client.Client, ioStreams cmduti

// workload Must found
ioStreams.Infof(" Traits:\n")

for traitType, traitInfo := range traitsStatus {
ioStreams.Infof(" - %s: %s", white.Sprint(traitType), traitInfo)
workloadStatus, _ := getWorkloadStatusFromAppConfig(appConfig, compName)
for _, tr := range workloadStatus.Traits {
traitType, traitInfo, err := traitCheckLoop(ctx, c, tr.Reference, compName, appConfig, app, 60*time.Second)
if err != nil {
ioStreams.Infof(" - %s%s: %s, err: %v", emojiFail, white.Sprint(traitType), traitInfo, err)
continue
}
ioStreams.Infof(" - %s%s: %s", emojiSucceed, white.Sprint(traitType), traitInfo)
}
ioStreams.Info("")
ioStreams.Infof(" Last Deployment:\n")
Expand All @@ -203,6 +210,44 @@ func printComponentStatus(ctx context.Context, c client.Client, ioStreams cmduti
return nil
}

// nolint
func traitCheckLoop(ctx context.Context, c client.Client, reference runtimev1alpha1.TypedReference, compName string, appConfig *v1alpha2.ApplicationConfiguration, app *api.Application, timeout time.Duration) (string, string, error) {
tr, err := oam2.GetUnstructured(ctx, c, appConfig.Namespace, reference)
if err != nil {
return "", "", err
}
traitType, ok := tr.GetLabels()[oam.TraitTypeLabel]
if !ok {
message, err := oam2.GetStatusFromObject(tr)
return traitType, message, err
}

checker := oam2.GetChecker(traitType, c)

// Health Check Loop For Trait
var message string
sHealthCheck := newTrackingSpinner(fmt.Sprintf("Checking %s status ...", traitType))
sHealthCheck.Start()
defer sHealthCheck.Stop()
CheckLoop:
for {
time.Sleep(trackingInterval)
var check oam2.CheckStatus
check, message, err = checker.Check(ctx, reference, compName, appConfig, app)
if err != nil {
message = red.Sprintf("%s check failed!", traitType)
return traitType, message, err
}
if check == oam2.StatusDone {
break CheckLoop
}
if time.Since(tr.GetCreationTimestamp().Time) >= timeout {
return traitType, fmt.Sprintf("Checking timeout: %s", message), nil
}
}
return traitType, message, nil
}

func healthCheckLoop(ctx context.Context, c client.Client, compName, appName string, env *types.EnvMeta) (HealthStatus, string, error) {
// Health Check Loop For Workload
var healthInfo string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
"fmt"
"time"

"github.com/pkg/errors"

"github.com/crossplane/crossplane-runtime/apis/core/v1alpha1"
"github.com/crossplane/crossplane-runtime/pkg/logging"
"github.com/go-logr/logr"
Expand Down Expand Up @@ -122,10 +124,17 @@ func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {

applog.Info("check application health status")
// check application health status
if err := handler.healthCheck(appfile); err != nil {
appCompStatus, healthy, err := handler.statusAggregate(appfile)
if err != nil {
app.Status.SetConditions(errorCondition("HealthCheck", err))
return handler.Err(err)
}
if !healthy {
app.Status.SetConditions(errorCondition("HealthCheck", errors.New("not healthy")))
app.Status.Services = appCompStatus
// unhealthy will check again after 10s
return ctrl.Result{RequeueAfter: time.Second * 10}, r.Status().Update(ctx, app)
}

app.Status.SetConditions(readyCondition("HealthCheck"))
app.Status.Phase = v1alpha2.ApplicationRunning
Expand Down
31 changes: 25 additions & 6 deletions pkg/controller/core.oam.dev/v1alpha2/application/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import (
"context"
"time"

"github.com/pkg/errors"

"github.com/oam-dev/kubevela/pkg/appfile"

runtimev1alpha1 "github.com/crossplane/crossplane-runtime/apis/core/v1alpha1"
"github.com/go-logr/logr"
v1 "k8s.io/api/core/v1"
Expand All @@ -15,7 +19,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
"github.com/oam-dev/kubevela/pkg/appfile"
"github.com/oam-dev/kubevela/pkg/dsl/process"
)

Expand Down Expand Up @@ -73,24 +76,40 @@ func (ret *reter) apply(ctx context.Context, ac *v1alpha2.ApplicationConfigurati
return ret.Sync(ctx, ac, comps)
}

func (ret *reter) healthCheck(appfile *appfile.Appfile) error {
func (ret *reter) statusAggregate(appfile *appfile.Appfile) ([]v1alpha2.ApplicationComponentStatus, bool, error) {
var appStatus []v1alpha2.ApplicationComponentStatus
var healthy = true
for _, wl := range appfile.Workloads {
var status = v1alpha2.ApplicationComponentStatus{
Name: wl.Name,
}
pCtx := process.NewContext(wl.Name)
if err := wl.EvalContext(pCtx); err != nil {
return err
return nil, false, err
}
for _, tr := range wl.Traits {
if err := tr.EvalContext(pCtx); err != nil {
return err
return nil, false, err
}
}
if err := wl.EvalHealth(pCtx, ret.c, appfile.Name); err != nil {
return err
//TODO(wonderflow): we should add a custom way to let the template say why it's unhealthy, only a bool flag is not enough
workloadHealth, err := wl.EvalHealth(pCtx, ret.c, appfile.Name)
if err != nil {
return nil, false, err
}
if !workloadHealth {
status.Healthy = false
healthy = false
}
statusMessage, err := wl.EvalStatus(pCtx, ret.c)
for _, trait := range wl.Traits {
if err := trait.EvalHealth(pCtx, ret.c, appfile.Name); err != nil {
return err
}
status, err := tr.EvalStatus(pCtx, cli, ns)
if err != nil {
return errors.WithMessagef(err, "[%s.%s] eval error", wl.Name, tr.Name)
}
}
}
return nil
Expand Down

0 comments on commit dc21fd1

Please sign in to comment.