github.com/oam-dev/kubevela@v1.9.11/pkg/workflow/providers/oam/apply.go (about)

     1  /*
     2  Copyright 2021 The KubeVela Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package oam
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"strings"
    23  
    24  	"cuelang.org/go/cue/cuecontext"
    25  	"github.com/pkg/errors"
    26  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    27  	"sigs.k8s.io/controller-runtime/pkg/client"
    28  
    29  	monitorContext "github.com/kubevela/pkg/monitor/context"
    30  	wfContext "github.com/kubevela/workflow/pkg/context"
    31  	"github.com/kubevela/workflow/pkg/cue/model/sets"
    32  	"github.com/kubevela/workflow/pkg/cue/model/value"
    33  	wfTypes "github.com/kubevela/workflow/pkg/types"
    34  
    35  	"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
    36  	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
    37  	"github.com/oam-dev/kubevela/pkg/appfile"
    38  	"github.com/oam-dev/kubevela/pkg/oam"
    39  )
    40  
    41  const (
    42  	// ProviderName is provider name for install.
    43  	ProviderName = "oam"
    44  )
    45  
    46  // ComponentApply apply oam component.
    47  type ComponentApply func(ctx context.Context, comp common.ApplicationComponent, patcher *value.Value, clusterName string, overrideNamespace string) (*unstructured.Unstructured, []*unstructured.Unstructured, bool, error)
    48  
    49  // ComponentRender render oam component.
    50  type ComponentRender func(ctx context.Context, comp common.ApplicationComponent, patcher *value.Value, clusterName string, overrideNamespace string) (*unstructured.Unstructured, []*unstructured.Unstructured, error)
    51  
    52  // ComponentHealthCheck health check oam component.
    53  type ComponentHealthCheck func(ctx context.Context, comp common.ApplicationComponent, patcher *value.Value, clusterName string, overrideNamespace string) (bool, *unstructured.Unstructured, []*unstructured.Unstructured, error)
    54  
    55  // WorkloadRenderer renderer to render application component into workload
    56  type WorkloadRenderer func(ctx context.Context, comp common.ApplicationComponent) (*appfile.Component, error)
    57  
    58  type provider struct {
    59  	render ComponentRender
    60  	apply  ComponentApply
    61  	app    *v1beta1.Application
    62  	af     *appfile.Appfile
    63  	cli    client.Client
    64  }
    65  
    66  // RenderComponent render component
    67  func (p *provider) RenderComponent(ctx monitorContext.Context, _ wfContext.Context, v *value.Value, _ wfTypes.Action) error {
    68  	comp, patcher, clusterName, overrideNamespace, err := lookUpCompInfo(v)
    69  	if err != nil {
    70  		return err
    71  	}
    72  	workload, traits, err := p.render(ctx, *comp, patcher, clusterName, overrideNamespace)
    73  	if err != nil {
    74  		return err
    75  	}
    76  
    77  	if workload != nil {
    78  		if err := v.FillObject(workload.Object, "output"); err != nil {
    79  			return errors.WithMessage(err, "FillOutput")
    80  		}
    81  	}
    82  
    83  	for _, trait := range traits {
    84  		name := trait.GetLabels()[oam.TraitResource]
    85  		if name != "" {
    86  			if err := v.FillObject(trait.Object, "outputs", name); err != nil {
    87  				return errors.WithMessage(err, "FillOutputs")
    88  			}
    89  		}
    90  	}
    91  
    92  	return nil
    93  }
    94  
    95  // ApplyComponent apply component.
    96  func (p *provider) ApplyComponent(ctx monitorContext.Context, _ wfContext.Context, v *value.Value, act wfTypes.Action) error {
    97  	comp, patcher, clusterName, overrideNamespace, err := lookUpCompInfo(v)
    98  	if err != nil {
    99  		return err
   100  	}
   101  	workload, traits, healthy, err := p.apply(ctx, *comp, patcher, clusterName, overrideNamespace)
   102  	if err != nil {
   103  		return err
   104  	}
   105  
   106  	if workload != nil {
   107  		if err := v.FillObject(workload.Object, "output"); err != nil {
   108  			return errors.WithMessage(err, "FillOutput")
   109  		}
   110  	}
   111  
   112  	for _, trait := range traits {
   113  		name := trait.GetLabels()[oam.TraitResource]
   114  		if name != "" {
   115  			if err := v.FillObject(trait.Object, "outputs", name); err != nil {
   116  				return errors.WithMessage(err, "FillOutputs")
   117  			}
   118  		}
   119  	}
   120  
   121  	waitHealthy, err := v.GetBool("waitHealthy")
   122  	if err != nil {
   123  		waitHealthy = true
   124  	}
   125  
   126  	if waitHealthy && !healthy {
   127  		act.Wait("wait healthy")
   128  	}
   129  	return nil
   130  }
   131  
   132  func lookUpCompInfo(v *value.Value) (*common.ApplicationComponent, *value.Value, string, string, error) {
   133  	compSettings, err := v.LookupValue("value")
   134  	if err != nil {
   135  		return nil, nil, "", "", err
   136  	}
   137  	comp := &common.ApplicationComponent{}
   138  
   139  	if err := compSettings.UnmarshalTo(comp); err != nil {
   140  		return nil, nil, "", "", err
   141  	}
   142  	patcher, err := v.LookupValue("patch")
   143  	if err != nil {
   144  		patcher = nil
   145  	}
   146  	clusterName, err := v.GetString("cluster")
   147  	if err != nil {
   148  		clusterName = ""
   149  	}
   150  	overrideNamespace, err := v.GetString("namespace")
   151  	if err != nil {
   152  		overrideNamespace = ""
   153  	}
   154  	return comp, patcher, clusterName, overrideNamespace, nil
   155  }
   156  
   157  // LoadComponent load component describe info in application.
   158  func (p *provider) LoadComponent(ctx monitorContext.Context, _ wfContext.Context, v *value.Value, _ wfTypes.Action) error {
   159  	app := &v1beta1.Application{}
   160  	// if specify `app`, use specified application otherwise use default application from provider
   161  	appSettings, err := v.LookupValue("app")
   162  	if err != nil {
   163  		if strings.Contains(err.Error(), "not exist") {
   164  			app = p.app
   165  		} else {
   166  			return err
   167  		}
   168  	} else {
   169  		if err := appSettings.UnmarshalTo(app); err != nil {
   170  			return err
   171  		}
   172  	}
   173  	for _, _comp := range app.Spec.Components {
   174  		comp, err := p.af.LoadDynamicComponent(ctx, p.cli, _comp.DeepCopy())
   175  		if err != nil {
   176  			return err
   177  		}
   178  		comp.Inputs = nil
   179  		comp.Outputs = nil
   180  		jt, err := json.Marshal(comp)
   181  		if err != nil {
   182  			return err
   183  		}
   184  		vs := string(jt)
   185  		cuectx := cuecontext.New()
   186  		val := cuectx.CompileString(vs)
   187  		if s, err := sets.OpenBaiscLit(val); err == nil {
   188  			v := cuectx.BuildFile(s)
   189  			str, err := sets.ToString(v)
   190  			if err != nil {
   191  				return err
   192  			}
   193  			vs = str
   194  		}
   195  		if err := v.FillRaw(vs, "value", comp.Name); err != nil {
   196  			return err
   197  		}
   198  	}
   199  	return nil
   200  }
   201  
   202  // LoadComponentInOrder load component describe info in application output will be a list with order defined in application.
   203  func (p *provider) LoadComponentInOrder(ctx monitorContext.Context, _ wfContext.Context, v *value.Value, _ wfTypes.Action) error {
   204  	app := &v1beta1.Application{}
   205  	// if specify `app`, use specified application otherwise use default application from provider
   206  	appSettings, err := v.LookupValue("app")
   207  	if err != nil {
   208  		if strings.Contains(err.Error(), "not exist") {
   209  			app = p.app
   210  		} else {
   211  			return err
   212  		}
   213  	} else {
   214  		if err := appSettings.UnmarshalTo(app); err != nil {
   215  			return err
   216  		}
   217  	}
   218  	comps := make([]common.ApplicationComponent, len(app.Spec.Components))
   219  	for idx, _comp := range app.Spec.Components {
   220  		comp, err := p.af.LoadDynamicComponent(ctx, p.cli, _comp.DeepCopy())
   221  		if err != nil {
   222  			return err
   223  		}
   224  		comp.Inputs = nil
   225  		comp.Outputs = nil
   226  		comps[idx] = *comp
   227  	}
   228  	if err := v.FillObject(comps, "value"); err != nil {
   229  		return err
   230  	}
   231  	return nil
   232  }
   233  
   234  // LoadPolicies load policy describe info in application.
   235  func (p *provider) LoadPolicies(_ monitorContext.Context, _ wfContext.Context, v *value.Value, _ wfTypes.Action) error {
   236  	for _, po := range p.app.Spec.Policies {
   237  		if err := v.FillObject(po, "value", po.Name); err != nil {
   238  			return err
   239  		}
   240  	}
   241  	return nil
   242  }
   243  
   244  // Install register handlers to provider discover.
   245  func Install(p wfTypes.Providers, app *v1beta1.Application, af *appfile.Appfile, cli client.Client, apply ComponentApply, render ComponentRender) {
   246  	prd := &provider{
   247  		render: render,
   248  		apply:  apply,
   249  		app:    app.DeepCopy(),
   250  		af:     af,
   251  		cli:    cli,
   252  	}
   253  	p.Register(ProviderName, map[string]wfTypes.Handler{
   254  		"component-render":    prd.RenderComponent,
   255  		"component-apply":     prd.ApplyComponent,
   256  		"load":                prd.LoadComponent,
   257  		"load-policies":       prd.LoadPolicies,
   258  		"load-comps-in-order": prd.LoadComponentInOrder,
   259  	})
   260  }