原创 吴就业 168 0 2023-07-23
本文为博主原创文章,未经博主允许不得转载。
本文链接:https://wujiuye.com/article/9e545bff4bd84b3ba88cbcfd0bf24de2
作者:吴就业
链接:https://wujiuye.com/article/9e545bff4bd84b3ba88cbcfd0bf24de2
来源:吴就业的网络日记
本文为博主原创文章,未经博主允许不得转载。
从generator#GenerateApplicationSetps生成应用工作流步骤方法可以看到下面这样一段代码:
handlerProviders := providers.NewProviders()
kube.Install(handlerProviders, h.r.Client, appLabels, &kube.Handlers{
Apply: h.Dispatch,
Delete: h.Delete,
})
configprovider.Install(handlerProviders, h.r.Client, func(ctx context.Context, resources []*unstructured.Unstructured, applyOptions []apply.ApplyOption) error {
for _, res := range resources {
res.SetLabels(util.MergeMapOverrideWithDst(res.GetLabels(), appLabels))
}
return h.resourceKeeper.Dispatch(ctx, resources, applyOptions)
})
oamProvider.Install(handlerProviders, app, af, h.r.Client, h.applyComponentFunc(
appParser, appRev, af), h.renderComponentFunc(appParser, appRev, af))
pCtx := velaprocess.NewContext(generateContextDataFromApp(app, appRev.Name))
renderer := func(ctx context.Context, comp common.ApplicationComponent) (*appfile.Workload, error) {
return appParser.ParseWorkloadFromRevisionAndClient(ctx, comp, appRev)
}
multiclusterProvider.Install(handlerProviders, h.r.Client, app, af,
h.applyComponentFunc(appParser, appRev, af),
h.checkComponentHealth(appParser, appRev, af),
renderer)
terraformProvider.Install(handlerProviders, app, renderer)
query.Install(handlerProviders, h.r.Client, nil)
terraformProvider、multiclusterProvider、oamProvider、configprovider、kube这些provider的Install方法注册了很多操作处理方法。这些方法就是提供给CUE中调用的方法。
创建工作流步骤Task的流程:
创建一个TaskDiscover,调用TaskDiscover的GetTaskGenerator方法生成TaskGenerator。TaskDiscover则根据工作流步骤指定的Type,通过LoadTemplate加载WorkflowStepDefinition。
apply-component工作流步骤的定义:
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
# Definition source cue file: vela-templates/definitions/internal/apply-component.cue
apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
custom.definition.oam.dev/category: Application Delivery
definition.oam.dev/description: Apply a specific component and its corresponding traits in application
labels:
custom.definition.oam.dev/scope: Application
name: apply-component
namespace: vela-system
spec:
schematic:
cue:
template: |
parameter: {
// +usage=Specify the component name to apply
component: string
// +usage=Specify the cluster
cluster: *"" | string
}
这个apply-component工作流步骤比较特殊,从generator#GenerateApplicationSetps方法也可以看出,apply-component会被转换为builtin-apply-component工作流步骤,代码如下:
StepConvertor: map[string]func(step workflowv1alpha1.WorkflowStep) (workflowv1alpha1.WorkflowStep, error){
wfTypes.WorkflowStepTypeApplyComponent: func(lstep workflowv1alpha1.WorkflowStep) (workflowv1alpha1.WorkflowStep, error) {
copierStep := lstep.DeepCopy()
if err := convertStepProperties(copierStep, app); err != nil {
return lstep, errors.WithMessage(err, "convert [apply-component]")
}
copierStep.Type = wfTypes.WorkflowStepTypeBuiltinApplyComponent
return *copierStep, nil
},
},
加载模版的流程:
generator#GenerateApplicationSetps方法中,调用generator#GenerateRunners方法,构造wfTypes.StepGeneratorOptions参数指定的TemplateLoader实现类为WorkflowStepTemplateRevisionLoader,代码如下:
runners, err := generator.GenerateRunners(ctx, instance, wfTypes.StepGeneratorOptions{
......
// 指定模版加载器
TemplateLoader: template.NewWorkflowStepTemplateRevisionLoader(appRev, h.r.dm),
......
})
LoadTemplate方法:
1.如果工作流步骤的类型为apply-component,转为builtin-apply-component工作流步骤,从源码目录的/workflow/template/static
目录下加载CUE模版代码。
2.其它则调用loadCapabilityDefinition方法加载CUE模版代码:
return &WorkflowStepLoader{
loadCapabilityDefinition: func(ctx context.Context, capName string) (*appfile.Template, error) {
return appfile.LoadTemplateFromRevision(capName, types.TypeWorkflowStep, rev, dm)
},
}
加载到CUE模版代码后,创建一个任务生成器(TaskGenerator),这个TaskGenerator做的事情:
1.根据模版,从工作流步骤解析出模版参数
2.创建任务:taskRunner
taskRunner做的事情:
1.执行一个pre hooks
2.执行工作流步骤
3.执行一些post hooks
任务taskRunner中调用doSteps方法执行工作流步骤:
一、从CUE模版(工作流步骤都是CUE模版)中获取#do
指定的方法,以及#provider
指定的提供者。
那么怎么知道#do
和 #provider
指定的是什么呢?我们以apply-component工作流步骤和deploy工作流步骤为例,看看它们的#do
和 #provider
是什么。
apply-component工作流步骤CUE模版:
import (
"vela/op"
)
oam: op.oam
// apply component and traits
apply: oam.#ApplyComponent & {
value: parameter.value
cluster: parameter.cluster
}
if apply.output != _|_ {
output: apply.output
}
if apply.outputs != _|_ {
outputs: apply.outputs
}
parameter: {
value: {...}
cluster: *"" | string
}
deploy工作流步骤CUE模版:
import (
"vela/op"
)
if parameter.auto == false {
suspend: op.#Suspend & {message: "Waiting approval to the deploy step "(context.stepName)""}
}
deploy: op.#Deploy & {
policies: parameter.policies
parallelism: parameter.parallelism
ignoreTerraformComponent: parameter.ignoreTerraformComponent
}
parameter: {
//+usage=If set to false, the workflow will suspend automatically before this step, default to be true.
auto: *true | bool
//+usage=Declare the policies that used for this deployment. If not specified, the components will be deployed to the hub cluster.
policies: *[] | [...string]
//+usage=Maximum number of concurrent delivered components.
parallelism: *5 | int
//+usage=If set false, this step will apply the components with the terraform workload.
ignoreTerraformComponent: *true | bool
}
从两个CUE模版中,我们都看不到#provider
和#do
,不过这两个模版都import了一个“vela/op”模版,我们先要找到这个vela/op模版。
我们找到kubevela源码项目的pkg/stdlib
目录,这里就是vela/op
的实现:
pkg/stdlib
-- pkgs
-- multicluster.cue
-- oam.cue
-- query.cue
-- terraform.cue
-- op.cue
-- packages.go
-- ql.cue
Import vela/op
导入的不仅是op.cue
这个模版,packages.go
中将op.cue
、pkgs/multicluster.cue
、pkgs/oam.cue
、pkgs/terraform.cue
这几个文件合并成一个后,形成vela/op
。
因此完整的代码应该是这样的:
#GetPlacementsFromTopologyPolicies: multicluster.#GetPlacementsFromTopologyPolicies
#Deploy: multicluster.#Deploy
#ApplyApplication: #Steps & {
load: oam.#LoadComponetsInOrder @step(1)
components: #Steps & {
for name, c in load.value {
"(name)": oam.#ApplyComponent & {
value: c
}
}
} @step(2)
}
// This operator will dispatch all the components in parallel when applying an application.
// Currently it works for Addon Observability to speed up the installation. It can also works for other applications, which
// needs to skip health check for components.
#ApplyApplicationInParallel: #Steps & {
load: oam.#LoadComponetsInOrder @step(1)
components: #Steps & {
for name, c in load.value {
"(name)": oam.#ApplyComponent & {
value: c
waitHealthy: false
}
}
} @step(2)
}
#ApplyComponent: oam.#ApplyComponent
#RenderComponent: oam.#RenderComponent
#ApplyComponentRemaining: #Steps & {
// exceptions specify the resources not to apply.
exceptions: [...string]
exceptions_: {for c in exceptions {"(c)": true}}
component: string
load: oam.#LoadComponets @step(1)
render: #Steps & {
rendered: oam.#RenderComponent & {
value: load.value[component]
}
comp: kube.#Apply & {
value: rendered.output
}
for name, c in rendered.outputs {
if exceptions_[name] == _|_ {
"(name)": kube.#Apply & {
value: c
}
}
}
} @step(2)
}
#ApplyRemaining: #Steps & {
// exceptions specify the resources not to apply.
exceptions: [...string]
exceptions_: {for c in exceptions {"(c)": true}}
load: oam.#LoadComponets @step(1)
components: #Steps & {
for name, c in load.value {
if exceptions_[name] == _|_ {
"(name)": oam.#ApplyComponent & {
value: c
}
}
}
} @step(2)
}
#ApplyEnvBindApp: multicluster.#ApplyEnvBindApp
#DeployCloudResource: terraform.#DeployCloudResource
#ShareCloudResource: terraform.#ShareCloudResource
#LoadPolicies: oam.#LoadPolicies
#ListClusters: multicluster.#ListClusters
#MakePlacementDecisions: multicluster.#MakePlacementDecisions
#PatchApplication: multicluster.#PatchApplication
#Load: oam.#LoadComponets
#LoadInOrder: oam.#LoadComponetsInOrder
multicluster: {
// deprecated
#Placement: {
clusterSelector?: {
labels?: [string]: string
name?: string
}
namespaceSelector?: {
labels?: [string]: string
name?: string
}
}
// deprecated
#PlacementDecision: {
namespace?: string
cluster?: string
}
// deprecated
#Component: {
name?: string
type?: string
properties?: {...}
traits?: [...{
type: string
disable?: bool
properties: {...}
}]
externalRevision?: string
dependsOn?: [...string]
}
// deprecated
#ReadPlacementDecisions: {
#provider: "multicluster"
#do: "read-placement-decisions"
inputs: {
policyName: string
envName: string
}
outputs: {
decisions?: [...#PlacementDecision]
}
}
// deprecated
#MakePlacementDecisions: {
#provider: "multicluster"
#do: "make-placement-decisions"
inputs: {
policyName: string
envName: string
placement: #Placement
}
outputs: {
decisions: [...#PlacementDecision]
}
}
// deprecated
#PatchApplication: {
#provider: "multicluster"
#do: "patch-application"
inputs: {
envName: string
patch?: components: [...#Component]
selector?: components: [...string]
}
outputs: {...}
...
}
// deprecated
#LoadEnvBindingEnv: #Steps & {
inputs: {
env: string
policy: string
}
loadPolicies: oam.#LoadPolicies @step(1)
policy_: string
envBindingPolicies: []
if inputs.policy == "" && loadPolicies.value != _|_ {
envBindingPolicies: [ for k, v in loadPolicies.value if v.type == "env-binding" {k}]
policy_: envBindingPolicies[0]
}
if inputs.policy != "" {
policy_: inputs.policy
}
loadPolicy: loadPolicies.value["(policy_)"]
envMap: {
for ev in loadPolicy.properties.envs {
"(ev.name)": ev
}
...
}
envConfig_: envMap["(inputs.env)"]
outputs: {
policy: policy_
envConfig: envConfig_
}
}
// deprecated
#PrepareEnvBinding: #Steps & {
inputs: {
env: string
policy: string
}
env_: inputs.env
policy_: inputs.policy
loadEnv: #LoadEnvBindingEnv & {
inputs: {
env: env_
policy: policy_
}
} @step(1)
envConfig: loadEnv.outputs.envConfig
placementDecisions: #MakePlacementDecisions & {
inputs: {
policyName: loadEnv.outputs.policy
envName: env_
placement: envConfig.placement
}
} @step(2)
patchedApp: #PatchApplication & {
inputs: {
envName: env_
if envConfig.selector != _|_ {
selector: envConfig.selector
}
if envConfig.patch != _|_ {
patch: envConfig.patch
}
}
} @step(3)
outputs: {
components: patchedApp.outputs.spec.components
decisions: placementDecisions.outputs.decisions
}
}
// deprecated
#ApplyComponentsToEnv: #Steps & {
inputs: {
decisions: [...#PlacementDecision]
components: [...#Component]
env: string
waitHealthy: bool
} @step(1)
outputs: #Steps & {
for decision in inputs.decisions {
for key, comp in inputs.components {
"(decision.cluster)-(decision.namespace)-(key)": #ApplyComponent & {
value: comp
if decision.cluster != _|_ {
cluster: decision.cluster
}
if decision.namespace != _|_ {
namespace: decision.namespace
}
waitHealthy: inputs.waitHealthy
env: inputs.env
} @step(1)
}
}
} @step(2)
}
// deprecated
#ApplyEnvBindApp: {
#do: "steps"
env: string
policy: string
app: string
namespace: string
parallel: bool
env_: env
policy_: policy
prepare: #PrepareEnvBinding & {
inputs: {
env: env_
policy: policy_
}
} @step(1)
apply: #ApplyComponentsToEnv & {
inputs: {
decisions: prepare.outputs.decisions
components: prepare.outputs.components
env: env_
waitHealthy: !parallel
}
} @step(2)
if parallel {
wait: #ApplyComponentsToEnv & {
inputs: {
decisions: prepare.outputs.decisions
components: prepare.outputs.components
env: env_
waitHealthy: true
}
} @step(3)
}
}
#ListClusters: {
#provider: "multicluster"
#do: "list-clusters"
outputs: {
clusters: [...string]
}
}
#GetPlacementsFromTopologyPolicies: {
#provider: "multicluster"
#do: "get-placements-from-topology-policies"
policies: [...string]
placements: [...{
cluster: string
namespace: string
}]
}
#Deploy: {
#provider: "multicluster"
#do: "deploy"
policies: [...string]
parallelism: int
ignoreTerraformComponent: bool
inlinePolicies: *[] | [...{...}]
}
}
oam: {
#ApplyComponent: {
#provider: "oam"
#do: "component-apply"
// +usage=The cluster to use
cluster: *"" | string
// +usage=The env to use
env: *"" | string
// +usage=The namespace to apply
namespace: *"" | string
// +usage=Whether to wait healthy of the applied component
waitHealthy: *true | bool
// +usage=The value of the component resource
value: {...}
// +usage=The patcher that will be applied to the resource, you can define the strategy of list merge through comments. Reference doc here: https://kubevela.io/docs/platform-engineers/traits/patch-trait#patch-in-workflow-step
patch?: {...}
...
}
#RenderComponent: {
#provider: "oam"
#do: "component-render"
cluster: *"" | string
env: *"" | string
namespace: *"" | string
value: {...}
patch?: {...}
output?: {...}
outputs?: {...}
...
}
#LoadComponets: {
#provider: "oam"
#do: "load"
// +usage=If specify `app`, use specified application to load its component resources otherwise use current application
app?: string
// +usage=The value of the components will be filled in this field after the action is executed, you can use value[componentName] to refer a specified component
value?: {...}
...
}
#LoadPolicies: {
#provider: "oam"
#do: "load-policies"
value?: {...}
...
}
#LoadComponetsInOrder: {
#provider: "oam"
#do: "load-comps-in-order"
...
}
}
query: {
#ListResourcesInApp: {
#do: "listResourcesInApp"
#provider: "query"
app: {
name: string
namespace: string
filter?: {
cluster?: string
clusterNamespace?: string
components?: [...string]
kind?: string
apiVersion?: string
}
withStatus?: bool
}
list?: [...{
cluster: string
component: string
revision: string
object: {...}
}]
...
}
#ListAppliedResources: {
#do: "listAppliedResources"
#provider: "query"
app: {
name: string
namespace: string
filter?: {
cluster?: string
clusterNamespace?: string
components?: [...string]
kind?: string
apiVersion?: string
}
}
list?: [...{
name: string
namespace?: string
cluster?: string
component?: string
trait?: string
kind?: string
uid?: string
apiVersion?: string
resourceVersion?: string
publishVersion?: string
deployVersion?: string
revision?: string
latest?: bool
resourceTree?: {
...
}
}]
...
}
#CollectPods: {
#do: "collectResources"
#provider: "query"
app: {
name: string
namespace: string
filter?: {
cluster?: string
clusterNamespace?: string
components?: [...string]
kind: "Pod"
apiVersion: "v1"
}
withTree: true
}
list: [...{...}]
...
}
#CollectServices: {
#do: "collectResources"
#provider: "query"
app: {
name: string
namespace: string
filter?: {
cluster?: string
clusterNamespace?: string
components?: [...string]
kind: "Service"
apiVersion: "v1"
}
withTree: true
}
list: [...{...}]
...
}
#SearchEvents: {
#do: "searchEvents"
#provider: "query"
value: {...}
cluster: string
...
}
#CollectLogsInPod: {
#do: "collectLogsInPod"
#provider: "query"
cluster: string
namespace: string
pod: string
options: {
container: string
previous: *false | bool
sinceSeconds: *null | int
sinceTime: *null | string
timestamps: *false | bool
tailLines: *null | int
limitBytes: *null | int
}
outputs?: {
logs?: string
err?: string
info?: {
fromDate: string
toDate: string
}
...
}
...
}
#CollectServiceEndpoints: {
#do: "collectServiceEndpoints"
#provider: "query"
app: {
name: string
namespace: string
filter?: {
cluster?: string
clusterNamespace?: string
components?: [...string]
}
withTree: true
}
list?: [...{
endpoint: {
protocol: string
appProtocol?: string
host?: string
port: int
portName?: string
path?: string
inner?: bool
}
ref: {...}
cluster?: string
component?: string
...
}]
...
}
#GetApplicationTree: {
#do: "listAppliedResources"
#provider: "query"
app: {
name: string
namespace: string
filter?: {
cluster?: string
clusterNamespace?: string
components?: [...string]
}
withTree: true
}
list?: [...{
name: string
namespace?: string
cluster?: string
component?: string
trait?: string
kind?: string
uid?: string
apiVersion?: string
resourceVersion?: string
publishVersion?: string
deployVersion?: string
revision?: string
latest?: bool
...
}]
...
}
}
terraform: {
#LoadTerraformComponents: {
#provider: "terraform"
#do: "load-terraform-components"
outputs: {
components: [...multicluster.#Component]
}
}
#GetConnectionStatus: {
#provider: "terraform"
#do: "get-connection-status"
inputs: {
componentName: string
}
outputs: {
healthy?: bool
}
}
#PrepareTerraformEnvBinding: #Steps & {
inputs: {
env: string
policy: string
}
env_: inputs.env
policy_: inputs.policy
prepare: multicluster.#PrepareEnvBinding & {
inputs: {
env: env_
policy: policy_
}
} @step(1)
loadTerraformComponents: #LoadTerraformComponents @step(2)
terraformComponentMap: {
for _, comp in loadTerraformComponents.outputs.components {
"(comp.name)": comp
}
...
}
components_: [ for comp in prepare.outputs.components if terraformComponentMap["(comp.name)"] != _|_ {comp}]
outputs: {
components: components_
decisions: prepare.outputs.decisions
}
}
#loadSecretInfo: {
component: {...}
appNamespace: string
name: string
namespace: string
env: string
if component.properties != _|_ && component.properties.writeConnectionSecretToRef != _|_ {
if component.properties.writeConnectionSecretToRef.name != _|_ {
name: component.properties.writeConnectionSecretToRef.name
}
if component.properties.writeConnectionSecretToRef.name == _|_ {
name: component.name
}
if component.properties.writeConnectionSecretToRef.namespace != _|_ {
namespace: component.properties.writeConnectionSecretToRef.namespace
}
if component.properties.writeConnectionSecretToRef.namespace == _|_ {
namespace: appNamespace
}
}
envName: "(name)-(env)"
}
#bindTerraformComponentToCluster: #Steps & {
comp: {...}
secret: {...}
env: string
decisions: [...{...}]
status: terraform.#GetConnectionStatus & {
inputs: componentName: "(comp.name)-(env)"
} @step(1)
read: kube.#Read & {
value: {
apiVersion: "v1"
kind: "Secret"
metadata: {
name: secret.envName
namespace: secret.namespace
...
}
...
}
} @step(2)
wait: {
#do: "wait"
continue: status.outputs.healthy && read.err == _|_
} @step(3)
sync: #Steps & {
for decision in decisions {
"(decision.cluster)-(decision.namespace)": kube.#Apply & {
cluster: decision.cluster
value: {
apiVersion: "v1"
kind: "Secret"
metadata: {
name: secret.name
if decision.namespace != _|_ && decision.namespace != "" {
namespace: decision.namespace
}
if decision.namespace == _|_ || decision.namespace == "" {
namespace: secret.namespace
}
...
}
type: "Opaque"
data: read.value.data
...
}
}
}
} @step(4)
}
#DeployCloudResource: {
#do: "steps"
env: string
name: string
policy: string
namespace: string
env_: env
policy_: policy
prepareDeploy: #PrepareTerraformEnvBinding & {
inputs: {
env: env_
policy: policy_
}
} @step(1)
deploy: #Steps & {
for comp in prepareDeploy.outputs.components {
"(comp.name)": #Steps & {
secretMeta: #loadSecretInfo & {
component: comp
env: env_
appNamespace: namespace
}
apply: #ApplyComponent & {
value: {
name: "(comp.name)-(env)"
properties: {
writeConnectionSecretToRef: {
name: secretMeta.envName
namespace: secretMeta.namespace
}
if comp.properties != _|_ {
for k, v in comp.properties {
if k != "writeConnectionSecretToRef" {
"(k)": v
}
}
}
...
}
for k, v in comp {
if k != "name" && k != "properties" {
"(k)": v
}
}
...
}
} @step(1)
comp_: comp
bind: #bindTerraformComponentToCluster & {
comp: comp_
secret: secretMeta
env: env_
decisions: prepareDeploy.outputs.decisions
} @step(2)
secret: bind.read.value
update: kube.#Apply & {
value: {
metadata: {
for k, v in secret.metadata {
if k != "labels" {
"(k)": v
}
}
labels: {
"app.oam.dev/name": name
"app.oam.dev/namespace": namespace
"app.oam.dev/component": comp.name
"app.oam.dev/env-name": env
"app.oam.dev/sync-alias": secretMeta.name
if secret.metadata.labels != _|_ {
for k, v in secret.metadata.labels {
if k != "app.oam.dev/name" && k != "app.oam.dev/sync-alias" && k != "app.oam.dev/env-name" {
"(k)": v
}
}
}
...
}
}
for k, v in secret {
if k != "metadata" {
"(k)": v
}
}
...
}
} @step(6)
}
}
...
} @step(2)
}
#ShareCloudResource: {
#do: "steps"
env: string
name: string
policy: string
namespace: string
namespace_: namespace
placements: [...multicluster.#PlacementDecision]
env_: env
policy_: policy
prepareBind: #PrepareTerraformEnvBinding & {
inputs: {
env: env_
policy: policy_
}
} @step(1)
decisions_: [ for placement in placements {
namespace: *"" | string
if placement.namespace != _|_ {
namespace: placement.namespace
}
if placement.namespace == _|_ {
namespace: namespace_
}
cluster: *"local" | string
if placement.cluster != _|_ {
cluster: placement.cluster
}
}]
deploy: #Steps & {
for comp in prepareBind.outputs.components {
"(comp.name)": #Steps & {
secretMeta: #loadSecretInfo & {
component: comp
env: env_
appNamespace: namespace
}
comp_: comp
bind: #bindTerraformComponentToCluster & {
comp: comp_
secret: secretMeta
env: env_
decisions: decisions_
} @step(1)
}
}
} @step(2)
}
}
现在我们再看apply-component
工作流步骤的CUE模版,其中“oam.#ApplyComponent”,就是指调用vela/op
的#ApplyComponent
方法,该方法CUE代码如下:
#ApplyComponent: {
#provider: "oam"
#do: "component-apply"
// +usage=The cluster to use
cluster: *"" | string
// +usage=The env to use
env: *"" | string
// +usage=The namespace to apply
namespace: *"" | string
// +usage=Whether to wait healthy of the applied component
waitHealthy: *true | bool
// +usage=The value of the component resource
value: {...}
// +usage=The patcher that will be applied to the resource, you can define the strategy of list merge through comments. Reference doc here: https://kubevela.io/docs/platform-engineers/traits/patch-trait#patch-in-workflow-step
patch?: {...}
...
}
至此,我们找到了apply-component工作流步骤模版的#provider
和#do
的值,apply-component工作流步骤的执行就是调用oam提供者的component-apply方法。
// Install register handlers to provider discover.
func Install(p wfTypes.Providers, app *v1beta1.Application, af *appfile.Appfile, cli client.Client, apply ComponentApply, render ComponentRender) {
prd := &provider{
render: render,
apply: apply,
app: app.DeepCopy(),
af: af,
cli: cli,
}
p.Register(ProviderName, map[string]wfTypes.Handler{
"component-render": prd.RenderComponent,
"component-apply": prd.ApplyComponent,
"load": prd.LoadComponent,
"load-policies": prd.LoadPolicies,
"load-comps-in-order": prd.LoadComponentInOrder,
})
}
以同样的方法找出deploy模版的op.#Deploy
指向的就是vela/op
的multicluster.#Deploy
方法,CUE代码如下:
#Deploy: {
#provider: "multicluster"
#do: "deploy"
policies: [...string]
parallelism: int
ignoreTerraformComponent: bool
inlinePolicies: *[] | [...{...}]
}
因此,deploy工作流步骤的执行就是调用multicluster提供者的deploy方法。
// Install register handlers to provider discover.
func Install(p wfTypes.Providers, c client.Client, app *v1beta1.Application, af *appfile.Appfile, apply oamProvider.ComponentApply, healthCheck oamProvider.ComponentHealthCheck, renderer oamProvider.WorkloadRenderer) {
prd := &provider{Client: c, app: app, af: af, apply: apply, healthCheck: healthCheck, renderer: renderer}
p.Register(ProviderName, map[string]wfTypes.Handler{
"read-placement-decisions": prd.ReadPlacementDecisions,
"make-placement-decisions": prd.MakePlacementDecisions,
"patch-application": prd.PatchApplication,
"list-clusters": prd.ListClusters,
"get-placements-from-topology-policies": prd.GetPlacementsFromTopologyPolicies,
"deploy": prd.Deploy,
})
}
理解了apply-component工作流步骤和deploy工作流步骤是如何与Go代码关联的,我们就很容易理解插件CUE代码做的是什么事情,也容器阅读原理去理解组件的Install细节了,更能帮忙我们以CUE写好插件。
声明:公众号、CSDN、掘金的曾用名:“Java艺术”,因此您可能看到一些早期的文章的图片有“Java艺术”的水印。
官方提供的工作流步骤有限,另外,对于自研的PaaS平台,我们需要借助工作流步骤实现一些例如存量项目基础设施导入、项目环境初始化、平台组件共享基础设施需要解决的差异对比审核、基础设施漂移等。
我们基于KubeVela开发的云原生应用交付平台,提供如初始化基础设施导入、中间件部署共用基础设施等相关能力的测试,需要依赖基础设施。虽然terraform是面向公司内部的混合云平台,但是测试都要跨部门配置效率太低了,而且这种模式无法支持持续测试。
如何Debug Terraform Controller;如何让Configuration可以指向私有仓库;为云资源编写ComponentDefinition;验证流程是否跑通。
kubevela安装一个Application的过程,就是执行工作流上的每个步骤的过程,并且当我们未配置工作流,kubevela会自动为组件的部署生成一个工作流步骤。
erraform-controller是一个专门负责terraform一类的组件"安装"的Operator,通过打包成helm,再封装成kubevela的Addon,由kubevela安装到管控集群,为其它terraform provider插件提供模块定义支持。
订阅
订阅新文章发布通知吧,不错过精彩内容!
输入邮箱,提交后我们会给您发送一封邮件,您需点击邮件中的链接完成订阅设置。