Feat: support force resource location with dispatch (#6033)

Signed-off-by: Somefive <yd219913@alibaba-inc.com>
This commit is contained in:
Somefive
2023-05-25 10:18:50 +08:00
committed by GitHub
parent 0df7803022
commit 4d81ba8909
9 changed files with 104 additions and 32 deletions

View File

@@ -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 != "" {

View File

@@ -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
}

View File

@@ -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)
}
}

View File

@@ -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 {

View File

@@ -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))
}

View File

@@ -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"]
}
}]

View File

@@ -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

View File

@@ -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())
})
})
})

View 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"]