github.com/oam-dev/kubevela@v1.9.11/pkg/workflow/providers/multicluster/multicluster.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 multicluster
    18  
    19  import (
    20  	"github.com/pkg/errors"
    21  	"sigs.k8s.io/controller-runtime/pkg/client"
    22  
    23  	monitorContext "github.com/kubevela/pkg/monitor/context"
    24  	wfContext "github.com/kubevela/workflow/pkg/context"
    25  	"github.com/kubevela/workflow/pkg/cue/model/value"
    26  	wfTypes "github.com/kubevela/workflow/pkg/types"
    27  
    28  	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
    29  	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
    30  	"github.com/oam-dev/kubevela/pkg/appfile"
    31  	"github.com/oam-dev/kubevela/pkg/multicluster"
    32  	pkgpolicy "github.com/oam-dev/kubevela/pkg/policy"
    33  	"github.com/oam-dev/kubevela/pkg/policy/envbinding"
    34  	oamProvider "github.com/oam-dev/kubevela/pkg/workflow/providers/oam"
    35  )
    36  
    37  const (
    38  	// ProviderName is provider name for install.
    39  	ProviderName = "multicluster"
    40  )
    41  
    42  type provider struct {
    43  	client.Client
    44  	app         *v1beta1.Application
    45  	af          *appfile.Appfile
    46  	apply       oamProvider.ComponentApply
    47  	healthCheck oamProvider.ComponentHealthCheck
    48  	renderer    oamProvider.WorkloadRenderer
    49  }
    50  
    51  // MakePlacementDecisions
    52  // Deprecated
    53  func (p *provider) MakePlacementDecisions(ctx monitorContext.Context, _ wfContext.Context, v *value.Value, _ wfTypes.Action) error {
    54  	policy, err := v.GetString("inputs", "policyName")
    55  	if err != nil {
    56  		return err
    57  	}
    58  	env, err := v.GetString("inputs", "envName")
    59  	if err != nil {
    60  		return err
    61  	}
    62  	val, err := v.LookupValue("inputs", "placement")
    63  	if err != nil {
    64  		return err
    65  	}
    66  
    67  	// TODO detect env change
    68  	placement := &v1alpha1.EnvPlacement{}
    69  	if err = val.UnmarshalTo(placement); err != nil {
    70  		return errors.Wrapf(err, "failed to parse placement while making placement decision")
    71  	}
    72  
    73  	var namespace, clusterName string
    74  	// check if namespace selector is valid
    75  	if placement.NamespaceSelector != nil {
    76  		if len(placement.NamespaceSelector.Labels) != 0 {
    77  			return errors.Errorf("invalid env %s: namespace selector in cluster-gateway does not support label selector for now", env)
    78  		}
    79  		namespace = placement.NamespaceSelector.Name
    80  	}
    81  	// check if cluster selector is valid
    82  	if placement.ClusterSelector != nil {
    83  		if len(placement.ClusterSelector.Labels) != 0 {
    84  			return errors.Errorf("invalid env %s: cluster selector does not support label selector for now", env)
    85  		}
    86  		clusterName = placement.ClusterSelector.Name
    87  	}
    88  	// set fallback cluster
    89  	if clusterName == "" {
    90  		clusterName = multicluster.ClusterLocalName
    91  	}
    92  	// check if target cluster exists
    93  	if clusterName != multicluster.ClusterLocalName {
    94  		if _, err := multicluster.GetVirtualCluster(ctx, p.Client, clusterName); err != nil {
    95  			return errors.Wrapf(err, "failed to get cluster %s for env %s", clusterName, env)
    96  		}
    97  	}
    98  	// write result back
    99  	decisions := []v1alpha1.PlacementDecision{{
   100  		Cluster:   clusterName,
   101  		Namespace: namespace,
   102  	}}
   103  	if err = envbinding.WritePlacementDecisions(p.app, policy, env, decisions); err != nil {
   104  		return err
   105  	}
   106  	return v.FillObject(map[string]interface{}{"decisions": decisions}, "outputs")
   107  }
   108  
   109  // PatchApplication
   110  // Deprecated
   111  func (p *provider) PatchApplication(_ monitorContext.Context, _ wfContext.Context, v *value.Value, _ wfTypes.Action) error {
   112  	env, err := v.GetString("inputs", "envName")
   113  	if err != nil {
   114  		return err
   115  	}
   116  	patch := v1alpha1.EnvPatch{}
   117  	selector := &v1alpha1.EnvSelector{}
   118  
   119  	obj, err := v.LookupValue("inputs", "patch")
   120  	if err == nil {
   121  		if err = obj.UnmarshalTo(&patch); err != nil {
   122  			return errors.Wrapf(err, "failed to unmarshal patch for env %s", env)
   123  		}
   124  	}
   125  	obj, err = v.LookupValue("inputs", "selector")
   126  	if err == nil {
   127  		if err = obj.UnmarshalTo(selector); err != nil {
   128  			return errors.Wrapf(err, "failed to unmarshal selector for env %s", env)
   129  		}
   130  	} else {
   131  		selector = nil
   132  	}
   133  
   134  	newApp, err := envbinding.PatchApplication(p.app, &patch, selector)
   135  	if err != nil {
   136  		return errors.Wrapf(err, "failed to patch app for env %s", env)
   137  	}
   138  	return v.FillObject(newApp, "outputs")
   139  }
   140  
   141  func (p *provider) ListClusters(ctx monitorContext.Context, _ wfContext.Context, v *value.Value, _ wfTypes.Action) error {
   142  	secrets, err := multicluster.ListExistingClusterSecrets(ctx, p.Client)
   143  	if err != nil {
   144  		return err
   145  	}
   146  	var clusters []string
   147  	for _, secret := range secrets {
   148  		clusters = append(clusters, secret.Name)
   149  	}
   150  	return v.FillObject(clusters, "outputs", "clusters")
   151  }
   152  
   153  func (p *provider) Deploy(ctx monitorContext.Context, _ wfContext.Context, v *value.Value, act wfTypes.Action) error {
   154  	param := DeployParameter{}
   155  	if err := v.CueValue().Decode(&param); err != nil {
   156  		return err
   157  	}
   158  	if param.Parallelism <= 0 {
   159  		return errors.Errorf("parallelism cannot be smaller than 1")
   160  	}
   161  	executor := NewDeployWorkflowStepExecutor(p.Client, p.af, p.apply, p.healthCheck, p.renderer, param)
   162  	healthy, reason, err := executor.Deploy(ctx)
   163  	if err != nil {
   164  		return err
   165  	}
   166  	if !healthy {
   167  		act.Wait(reason)
   168  	}
   169  	return nil
   170  }
   171  
   172  func (p *provider) GetPlacementsFromTopologyPolicies(ctx monitorContext.Context, _ wfContext.Context, v *value.Value, _ wfTypes.Action) error {
   173  	policyNames, err := v.GetStringSlice("policies")
   174  	if err != nil {
   175  		return err
   176  	}
   177  	policies, err := selectPolicies(p.af.Policies, policyNames)
   178  	if err != nil {
   179  		return err
   180  	}
   181  	placements, err := pkgpolicy.GetPlacementsFromTopologyPolicies(ctx, p.Client, p.af.Namespace, policies, true)
   182  	if err != nil {
   183  		return err
   184  	}
   185  	return v.FillObject(placements, "placements")
   186  }
   187  
   188  // Install register handlers to provider discover.
   189  func Install(p wfTypes.Providers, c client.Client, app *v1beta1.Application, af *appfile.Appfile, apply oamProvider.ComponentApply, healthCheck oamProvider.ComponentHealthCheck, renderer oamProvider.WorkloadRenderer) {
   190  	prd := &provider{Client: c, app: app, af: af, apply: apply, healthCheck: healthCheck, renderer: renderer}
   191  	p.Register(ProviderName, map[string]wfTypes.Handler{
   192  		"make-placement-decisions":              prd.MakePlacementDecisions,
   193  		"patch-application":                     prd.PatchApplication,
   194  		"list-clusters":                         prd.ListClusters,
   195  		"get-placements-from-topology-policies": prd.GetPlacementsFromTopologyPolicies,
   196  		"deploy":                                prd.Deploy,
   197  	})
   198  }