mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-14 10:00:06 +00:00
Feat: support force resource location with dispatch (#6033)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
This commit is contained in:
@@ -429,7 +429,7 @@ func overrideTraits(appRev *v1beta1.ApplicationRevision, readyTraits []*unstruct
|
||||
for index, readyTrait := range readyTraits {
|
||||
for _, trait := range appRev.Spec.TraitDefinitions {
|
||||
if trait.Spec.ControlPlaneOnly && trait.Name == readyTrait.GetLabels()[oam.TraitTypeLabel] {
|
||||
oam.SetCluster(traits[index], "local")
|
||||
oam.SetCluster(traits[index], multicluster.ClusterLocalName)
|
||||
traits[index].SetNamespace(appRev.GetNamespace())
|
||||
break
|
||||
}
|
||||
@@ -481,9 +481,9 @@ func renderComponentsAndTraits(client client.Client, manifest *types.ComponentMa
|
||||
return nil, nil, errors.WithMessage(err, "assemble resources before apply fail")
|
||||
}
|
||||
if clusterName != "" {
|
||||
oam.SetCluster(readyWorkload, clusterName)
|
||||
oam.SetClusterIfEmpty(readyWorkload, clusterName)
|
||||
for _, readyTrait := range readyTraits {
|
||||
oam.SetCluster(readyTrait, clusterName)
|
||||
oam.SetClusterIfEmpty(readyTrait, clusterName)
|
||||
}
|
||||
}
|
||||
if overrideNamespace != "" {
|
||||
|
||||
@@ -17,12 +17,14 @@ limitations under the License.
|
||||
package definition
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"cuelang.org/go/cue/build"
|
||||
"cuelang.org/go/cue/cuecontext"
|
||||
"github.com/kubevela/pkg/multicluster"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
@@ -160,6 +162,13 @@ func (wd *workloadDef) Complete(ctx process.Context, abstractTemplate string, pa
|
||||
return nil
|
||||
}
|
||||
|
||||
func withCluster(ctx context.Context, o client.Object) context.Context {
|
||||
if cluster := oam.GetCluster(o); cluster != "" {
|
||||
return multicluster.WithCluster(ctx, cluster)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (wd *workloadDef) getTemplateContext(ctx process.Context, cli client.Reader, accessor util.NamespaceAccessor) (map[string]interface{}, error) {
|
||||
baseLabels := GetBaseContextLabels(ctx)
|
||||
var root = initRoot(baseLabels)
|
||||
@@ -171,7 +180,8 @@ func (wd *workloadDef) getTemplateContext(ctx process.Context, cli client.Reader
|
||||
return nil, err
|
||||
}
|
||||
// workload main resource will have a unique label("app.oam.dev/resourceType"="WORKLOAD") in per component/app level
|
||||
object, err := getResourceFromObj(ctx, componentWorkload, cli, accessor.For(componentWorkload), util.MergeMapOverrideWithDst(map[string]string{
|
||||
_ctx := withCluster(ctx.GetCtx(), componentWorkload)
|
||||
object, err := getResourceFromObj(_ctx, ctx, componentWorkload, cli, accessor.For(componentWorkload), util.MergeMapOverrideWithDst(map[string]string{
|
||||
oam.LabelOAMResourceType: oam.ResourceTypeWorkload,
|
||||
}, commonLabels), "")
|
||||
if err != nil {
|
||||
@@ -191,7 +201,8 @@ func (wd *workloadDef) getTemplateContext(ctx process.Context, cli client.Reader
|
||||
return nil, err
|
||||
}
|
||||
// AuxiliaryWorkload will have a unique label("trait.oam.dev/resource"="name of outputs") in per component/app level
|
||||
object, err := getResourceFromObj(ctx, traitRef, cli, accessor.For(traitRef), util.MergeMapOverrideWithDst(map[string]string{
|
||||
_ctx := withCluster(ctx.GetCtx(), traitRef)
|
||||
object, err := getResourceFromObj(_ctx, ctx, traitRef, cli, accessor.For(traitRef), util.MergeMapOverrideWithDst(map[string]string{
|
||||
oam.TraitTypeLabel: AuxiliaryWorkload,
|
||||
}, commonLabels), assist.Name)
|
||||
if err != nil {
|
||||
@@ -451,7 +462,8 @@ func (td *traitDef) getTemplateContext(ctx process.Context, cli client.Reader, a
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
object, err := getResourceFromObj(ctx, traitRef, cli, accessor.For(traitRef), util.MergeMapOverrideWithDst(map[string]string{
|
||||
_ctx := withCluster(ctx.GetCtx(), traitRef)
|
||||
object, err := getResourceFromObj(_ctx, ctx, traitRef, cli, accessor.For(traitRef), util.MergeMapOverrideWithDst(map[string]string{
|
||||
oam.TraitTypeLabel: assist.Type,
|
||||
}, commonLabels), assist.Name)
|
||||
if err != nil {
|
||||
@@ -479,24 +491,24 @@ func (td *traitDef) GetTemplateContext(ctx process.Context, cli client.Client, a
|
||||
return td.getTemplateContext(ctx, cli, accessor)
|
||||
}
|
||||
|
||||
func getResourceFromObj(ctx process.Context, obj *unstructured.Unstructured, client client.Reader, namespace string, labels map[string]string, outputsResource string) (map[string]interface{}, error) {
|
||||
func getResourceFromObj(ctx context.Context, pctx process.Context, obj *unstructured.Unstructured, client client.Reader, namespace string, labels map[string]string, outputsResource string) (map[string]interface{}, error) {
|
||||
if outputsResource != "" {
|
||||
labels[oam.TraitResource] = outputsResource
|
||||
}
|
||||
if obj.GetName() != "" {
|
||||
u, err := util.GetObjectGivenGVKAndName(ctx.GetCtx(), client, obj.GroupVersionKind(), namespace, obj.GetName())
|
||||
u, err := util.GetObjectGivenGVKAndName(ctx, client, obj.GroupVersionKind(), namespace, obj.GetName())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return u.Object, nil
|
||||
}
|
||||
if ctxName := ctx.GetData(model.ContextName).(string); ctxName != "" {
|
||||
u, err := util.GetObjectGivenGVKAndName(ctx.GetCtx(), client, obj.GroupVersionKind(), namespace, ctxName)
|
||||
if ctxName := pctx.GetData(model.ContextName).(string); ctxName != "" {
|
||||
u, err := util.GetObjectGivenGVKAndName(ctx, client, obj.GroupVersionKind(), namespace, ctxName)
|
||||
if err == nil {
|
||||
return u.Object, nil
|
||||
}
|
||||
}
|
||||
list, err := util.GetObjectsGivenGVKAndLabels(ctx.GetCtx(), client, obj.GroupVersionKind(), namespace, labels)
|
||||
list, err := util.GetObjectsGivenGVKAndLabels(ctx, client, obj.GroupVersionKind(), namespace, labels)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -72,9 +72,7 @@ func ResourcesWithClusterName(clusterName string, objs ...*unstructured.Unstruct
|
||||
var _objs []*unstructured.Unstructured
|
||||
for _, obj := range objs {
|
||||
if obj != nil {
|
||||
if oam.GetCluster(obj) == "" {
|
||||
oam.SetCluster(obj, clusterName)
|
||||
}
|
||||
oam.SetClusterIfEmpty(obj, clusterName)
|
||||
_objs = append(_objs, obj)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,13 @@ func SetCluster(o client.Object, clusterName string) {
|
||||
meta.AddLabels(o, map[string]string{LabelAppCluster: clusterName})
|
||||
}
|
||||
|
||||
// SetClusterIfEmpty set cluster label to object if the label is empty
|
||||
func SetClusterIfEmpty(o client.Object, clusterName string) {
|
||||
if GetCluster(o) == "" {
|
||||
SetCluster(o, clusterName)
|
||||
}
|
||||
}
|
||||
|
||||
// GetCluster get cluster from object
|
||||
func GetCluster(o client.Object) string {
|
||||
if labels := o.GetLabels(); labels != nil {
|
||||
|
||||
@@ -28,6 +28,6 @@ func TestGetSetCluster(t *testing.T) {
|
||||
deploy := &v1.Deployment{}
|
||||
r.Equal("", GetCluster(deploy))
|
||||
clusterName := "cluster"
|
||||
SetCluster(deploy, clusterName)
|
||||
SetClusterIfEmpty(deploy, clusterName)
|
||||
r.Equal(clusterName, GetCluster(deploy))
|
||||
}
|
||||
|
||||
@@ -92,8 +92,8 @@ import (
|
||||
_category: key
|
||||
}
|
||||
_cluster: *"local" | string
|
||||
if r.metadata.annotations != _|_ if r.metadata.annotations["app.oam.dev/cluster"] != _|_ {
|
||||
_cluster: r.metadata.annotations["app.oam.dev/cluster"]
|
||||
if r.metadata.labels != _|_ if r.metadata.labels["app.oam.dev/cluster"] != _|_ {
|
||||
_cluster: r.metadata.labels["app.oam.dev/cluster"]
|
||||
}
|
||||
}]
|
||||
|
||||
|
||||
@@ -65,15 +65,14 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
adoptTypeNative = "native"
|
||||
adoptTypeHelm = "helm"
|
||||
adoptModeReadOnly = v1alpha1.ReadOnlyPolicyType
|
||||
adoptModeTakeOver = v1alpha1.TakeOverPolicyType
|
||||
helmDriverEnvKey = "HELM_DRIVER"
|
||||
defaultHelmDriver = "secret"
|
||||
adoptCUETempVal = "adopt"
|
||||
adoptCUETempFunc = "#Adopt"
|
||||
defaultLocalCluster = "local"
|
||||
adoptTypeNative = "native"
|
||||
adoptTypeHelm = "helm"
|
||||
adoptModeReadOnly = v1alpha1.ReadOnlyPolicyType
|
||||
adoptModeTakeOver = v1alpha1.TakeOverPolicyType
|
||||
helmDriverEnvKey = "HELM_DRIVER"
|
||||
defaultHelmDriver = "secret"
|
||||
adoptCUETempVal = "adopt"
|
||||
adoptCUETempFunc = "#Adopt"
|
||||
)
|
||||
|
||||
//go:embed adopt-templates/default.cue
|
||||
@@ -155,7 +154,7 @@ func (opt *AdoptOptions) parseResourceRef(f velacmd.Factory, cmd *cobra.Command,
|
||||
return nil, fmt.Errorf("no mappings found for resource %s: %w", arg, err)
|
||||
}
|
||||
mapping := mappings[0]
|
||||
or := &resourceRef{GroupVersionKind: gvk, Cluster: defaultLocalCluster, Arg: arg}
|
||||
or := &resourceRef{GroupVersionKind: gvk, Cluster: multicluster.Local, Arg: arg}
|
||||
switch len(parts) {
|
||||
case 2:
|
||||
or.Name = parts[1]
|
||||
@@ -446,7 +445,7 @@ func (opt *AdoptOptions) loadNative(f velacmd.Factory, cmd *cobra.Command) error
|
||||
if err := f.Client().Get(multicluster.WithCluster(cmd.Context(), ref.Cluster), apitypes.NamespacedName{Namespace: ref.Namespace, Name: ref.Name}, obj); err != nil {
|
||||
return fmt.Errorf("fail to get resource for %s: %w", ref.Arg, err)
|
||||
}
|
||||
_ = k8s.AddAnnotation(obj, oam.LabelAppCluster, ref.Cluster)
|
||||
_ = k8s.AddLabel(obj, oam.LabelAppCluster, ref.Cluster)
|
||||
opt.Resources = append(opt.Resources, obj)
|
||||
}
|
||||
return nil
|
||||
@@ -472,10 +471,7 @@ func (opt *AdoptOptions) loadHelm() error {
|
||||
klog.Warningf("unable to decode object %s: %s", val, err)
|
||||
continue
|
||||
}
|
||||
annos := map[string]string{
|
||||
oam.LabelAppCluster: defaultLocalCluster,
|
||||
}
|
||||
obj.SetAnnotations(annos)
|
||||
_ = k8s.AddLabel(obj, oam.LabelAppCluster, multicluster.Local)
|
||||
objs = append(objs, obj)
|
||||
}
|
||||
opt.Resources = objs
|
||||
|
||||
@@ -1246,5 +1246,35 @@ var _ = Describe("Test multicluster scenario", func() {
|
||||
Expect(k8sClient.Get(workerCtx, appKey, obj)).Should(Succeed())
|
||||
Expect(obj.Object["spec"].(map[string]interface{})["key"]).Should(Equal("value"))
|
||||
})
|
||||
|
||||
It("Test application with fixed cluster to dispatch", func() {
|
||||
ctx := context.Background()
|
||||
app := &v1beta1.Application{}
|
||||
bs, err := os.ReadFile("./testdata/app/app-with-fixed-location.yaml")
|
||||
Expect(err).Should(Succeed())
|
||||
Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
|
||||
app.SetNamespace(namespace)
|
||||
Eventually(func(g Gomega) {
|
||||
g.Expect(k8sClient.Create(ctx, app)).Should(Succeed())
|
||||
}).WithPolling(2 * time.Second).WithTimeout(5 * time.Second).Should(Succeed())
|
||||
appKey := client.ObjectKeyFromObject(app)
|
||||
Eventually(func(g Gomega) {
|
||||
_app := &v1beta1.Application{}
|
||||
g.Expect(k8sClient.Get(ctx, appKey, _app)).Should(Succeed())
|
||||
g.Expect(_app.Status.Phase).Should(Equal(common.ApplicationRunning))
|
||||
}).WithPolling(2 * time.Second).WithTimeout(20 * time.Second).Should(Succeed())
|
||||
Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: "x"}, &corev1.ConfigMap{})).Should(Succeed())
|
||||
Expect(k8sClient.Get(workerCtx, types.NamespacedName{Namespace: namespace, Name: "y"}, &corev1.ConfigMap{})).Should(Succeed())
|
||||
|
||||
By("Deleting")
|
||||
_app := &v1beta1.Application{}
|
||||
Expect(k8sClient.Get(ctx, appKey, _app)).Should(Succeed())
|
||||
Expect(k8sClient.Delete(ctx, _app)).Should(Succeed())
|
||||
Eventually(func(g Gomega) {
|
||||
g.Expect(kerrors.IsNotFound(k8sClient.Get(ctx, appKey, _app))).Should(BeTrue())
|
||||
}).WithPolling(2 * time.Second).WithTimeout(20 * time.Second).Should(Succeed())
|
||||
Expect(kerrors.IsNotFound(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: "x"}, &corev1.ConfigMap{}))).Should(BeTrue())
|
||||
Expect(kerrors.IsNotFound(k8sClient.Get(workerCtx, types.NamespacedName{Namespace: namespace, Name: "y"}, &corev1.ConfigMap{}))).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
29
test/e2e-multicluster-test/testdata/app/app-with-fixed-location.yaml
vendored
Normal file
29
test/e2e-multicluster-test/testdata/app/app-with-fixed-location.yaml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: app
|
||||
spec:
|
||||
components:
|
||||
- type: k8s-objects
|
||||
name: app
|
||||
properties:
|
||||
objects:
|
||||
- apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: "x"
|
||||
labels:
|
||||
app.oam.dev/cluster: local
|
||||
data:
|
||||
key: "x"
|
||||
- apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: "y"
|
||||
data:
|
||||
key: "y"
|
||||
policies:
|
||||
- type: topology
|
||||
name: topology
|
||||
properties:
|
||||
clusters: ["cluster-worker"]
|
||||
Reference in New Issue
Block a user