github.com/oam-dev/kubevela@v1.9.11/references/cli/adopt.go (about) 1 /* 2 Copyright 2022 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 cli 18 19 import ( 20 "context" 21 _ "embed" 22 "encoding/json" 23 "fmt" 24 "os" 25 "strings" 26 "time" 27 28 "cuelang.org/go/cue" 29 "github.com/kubevela/pkg/cue/cuex" 30 "github.com/spf13/cobra" 31 "helm.sh/helm/v3/pkg/action" 32 "helm.sh/helm/v3/pkg/release" 33 "helm.sh/helm/v3/pkg/releaseutil" 34 "helm.sh/helm/v3/pkg/storage" 35 appsv1 "k8s.io/api/apps/v1" 36 "k8s.io/apimachinery/pkg/api/meta" 37 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 38 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 39 "k8s.io/apimachinery/pkg/runtime/schema" 40 apitypes "k8s.io/apimachinery/pkg/types" 41 "k8s.io/apimachinery/pkg/util/wait" 42 "k8s.io/klog/v2" 43 "k8s.io/kubectl/pkg/util/i18n" 44 "k8s.io/kubectl/pkg/util/templates" 45 "k8s.io/utils/strings/slices" 46 "sigs.k8s.io/controller-runtime/pkg/client" 47 "sigs.k8s.io/yaml" 48 49 "github.com/kubevela/pkg/util/k8s" 50 "github.com/kubevela/pkg/util/resourcetopology" 51 velaslices "github.com/kubevela/pkg/util/slices" 52 53 "github.com/kubevela/pkg/multicluster" 54 55 "github.com/oam-dev/kubevela/apis/core.oam.dev/common" 56 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1" 57 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" 58 "github.com/oam-dev/kubevela/apis/types" 59 velacmd "github.com/oam-dev/kubevela/pkg/cmd" 60 cmdutil "github.com/oam-dev/kubevela/pkg/cmd/util" 61 "github.com/oam-dev/kubevela/pkg/oam" 62 "github.com/oam-dev/kubevela/pkg/utils/apply" 63 "github.com/oam-dev/kubevela/pkg/utils/env" 64 "github.com/oam-dev/kubevela/pkg/utils/util" 65 ) 66 67 const ( 68 adoptTypeNative = "native" 69 adoptTypeHelm = "helm" 70 adoptModeReadOnly = v1alpha1.ReadOnlyPolicyType 71 adoptModeTakeOver = v1alpha1.TakeOverPolicyType 72 helmDriverEnvKey = "HELM_DRIVER" 73 defaultHelmDriver = "secret" 74 adoptCUETempVal = "adopt" 75 adoptCUETempFunc = "#Adopt" 76 ) 77 78 //go:embed adopt-templates/default.cue 79 var defaultAdoptTemplate string 80 81 //go:embed resource-topology/builtin-rule.cue 82 var defaultResourceTopologyRule string 83 84 var ( 85 adoptTypes = []string{adoptTypeNative, adoptTypeHelm} 86 adoptModes = []string{adoptModeReadOnly, adoptModeTakeOver} 87 ) 88 89 type resourceRef struct { 90 schema.GroupVersionKind 91 apitypes.NamespacedName 92 Cluster string 93 Arg string 94 } 95 96 // AdoptOptions options for vela adopt command 97 type AdoptOptions struct { 98 Type string `json:"type"` 99 Mode string `json:"mode"` 100 AppName string `json:"appName"` 101 AppNamespace string `json:"appNamespace"` 102 103 HelmReleaseName string 104 HelmReleaseNamespace string 105 HelmDriver string 106 HelmConfig *action.Configuration 107 HelmStore *storage.Storage 108 HelmRelease *release.Release 109 HelmReleaseRevisions []*release.Release 110 111 NativeResourceRefs []*resourceRef 112 113 Apply bool 114 Recycle bool 115 Yes bool 116 All bool 117 118 AdoptTemplateFile string 119 AdoptTemplate string 120 AdoptTemplateCUEValue cue.Value 121 122 ResourceTopologyRuleFile string 123 ResourceTopologyRule string 124 AllGVKs []schema.GroupVersionKind 125 126 Resources []*unstructured.Unstructured `json:"resources"` 127 128 util.IOStreams 129 } 130 131 func (opt *AdoptOptions) parseResourceGVK(f velacmd.Factory, arg string) (schema.GroupVersionKind, error) { 132 _, gr := schema.ParseResourceArg(arg) 133 gvks, err := f.Client().RESTMapper().KindsFor(gr.WithVersion("")) 134 if err != nil { 135 return schema.GroupVersionKind{}, fmt.Errorf("failed to find types for resource %s: %w", arg, err) 136 } 137 if len(gvks) == 0 { 138 return schema.GroupVersionKind{}, fmt.Errorf("no schema found for resource %s: %w", arg, err) 139 } 140 return gvks[0], nil 141 } 142 143 func (opt *AdoptOptions) parseResourceRef(f velacmd.Factory, cmd *cobra.Command, arg string) (*resourceRef, error) { 144 parts := strings.Split(arg, "/") 145 gvk, err := opt.parseResourceGVK(f, parts[0]) 146 if err != nil { 147 return nil, err 148 } 149 mappings, err := f.Client().RESTMapper().RESTMappings(gvk.GroupKind(), gvk.Version) 150 if err != nil { 151 return nil, fmt.Errorf("failed to find mappings for resource %s: %w", arg, err) 152 } 153 if len(mappings) == 0 { 154 return nil, fmt.Errorf("no mappings found for resource %s: %w", arg, err) 155 } 156 mapping := mappings[0] 157 or := &resourceRef{GroupVersionKind: gvk, Cluster: multicluster.Local, Arg: arg} 158 switch len(parts) { 159 case 2: 160 or.Name = parts[1] 161 if mapping.Scope.Name() == meta.RESTScopeNameNamespace { 162 or.Namespace = velacmd.GetNamespace(f, cmd) 163 if or.Namespace == "" { 164 or.Namespace = env.DefaultEnvNamespace 165 } 166 } 167 case 3: 168 or.Namespace = parts[1] 169 or.Name = parts[2] 170 case 4: 171 or.Cluster = parts[1] 172 or.Namespace = parts[2] 173 or.Name = parts[3] 174 default: 175 return nil, fmt.Errorf("resource should be like <type>/<name> or <type>/<namespace>/<name> or <type>/<cluster>/<namespace>/<name>") 176 } 177 return or, nil 178 } 179 180 // Init . 181 func (opt *AdoptOptions) Init(f velacmd.Factory, cmd *cobra.Command, args []string) (err error) { 182 if opt.All { 183 if len(args) > 0 { 184 for _, arg := range args { 185 gvk, err := opt.parseResourceGVK(f, arg) 186 if err != nil { 187 return err 188 } 189 opt.AllGVKs = append(opt.AllGVKs, gvk) 190 apiVersion, kind := gvk.ToAPIVersionAndKind() 191 _, _ = fmt.Fprintf(opt.Out, "Adopt all %s/%s resources\n", apiVersion, kind) 192 } 193 } 194 if len(opt.AllGVKs) == 0 { 195 opt.AllGVKs = []schema.GroupVersionKind{ 196 appsv1.SchemeGroupVersion.WithKind("Deployment"), 197 appsv1.SchemeGroupVersion.WithKind("StatefulSet"), 198 appsv1.SchemeGroupVersion.WithKind("DaemonSet"), 199 } 200 _, _ = opt.Out.Write([]byte("No arguments specified, adopt all Deployment/StatefulSet/DaemonSet resources by default\n")) 201 } 202 } 203 if opt.AdoptTemplateFile != "" { 204 bs, err := os.ReadFile(opt.AdoptTemplateFile) 205 if err != nil { 206 return fmt.Errorf("failed to load file %s", opt.AdoptTemplateFile) 207 } 208 opt.AdoptTemplate = string(bs) 209 } else { 210 opt.AdoptTemplate = defaultAdoptTemplate 211 } 212 if opt.ResourceTopologyRuleFile != "" { 213 bs, err := os.ReadFile(opt.ResourceTopologyRuleFile) 214 if err != nil { 215 return fmt.Errorf("failed to load file %s", opt.ResourceTopologyRuleFile) 216 } 217 opt.ResourceTopologyRule = string(bs) 218 } else { 219 opt.ResourceTopologyRule = defaultResourceTopologyRule 220 } 221 opt.AppNamespace = velacmd.GetNamespace(f, cmd) 222 opt.AdoptTemplateCUEValue, err = cuex.CompileString(cmd.Context(), fmt.Sprintf("%s\n\n%s: %s", opt.AdoptTemplate, adoptCUETempVal, adoptCUETempFunc)) 223 if err != nil { 224 return fmt.Errorf("failed to compile template: %w", err) 225 } 226 switch opt.Type { 227 case adoptTypeNative: 228 if opt.Recycle { 229 return fmt.Errorf("native resource adoption does not support --recycle flag") 230 } 231 case adoptTypeHelm: 232 if len(opt.HelmDriver) == 0 { 233 opt.HelmDriver = os.Getenv(helmDriverEnvKey) 234 } 235 if len(opt.HelmDriver) == 0 { 236 opt.HelmDriver = defaultHelmDriver 237 } 238 actionConfig := new(action.Configuration) 239 opt.HelmReleaseNamespace = opt.AppNamespace 240 if err := actionConfig.Init( 241 util.NewRestConfigGetterByConfig(f.Config(), opt.HelmReleaseNamespace), 242 opt.HelmReleaseNamespace, 243 opt.HelmDriver, 244 klog.Infof); err != nil { 245 return err 246 } 247 opt.HelmConfig = actionConfig 248 default: 249 return fmt.Errorf("invalid adopt type: %s, available types: [%s]", opt.Type, strings.Join(adoptTypes, ", ")) 250 } 251 if slices.Index(adoptModes, opt.Mode) < 0 { 252 return fmt.Errorf("invalid adopt mode: %s, available modes: [%s]", opt.Mode, strings.Join(adoptModes, ", ")) 253 } 254 if opt.Recycle && !opt.Apply { 255 return fmt.Errorf("old data can only be recycled when the adoption application is applied") 256 } 257 return nil 258 } 259 260 // MultipleRun . 261 func (opt *AdoptOptions) MultipleRun(f velacmd.Factory, cmd *cobra.Command) error { 262 resources := make([][]*unstructured.Unstructured, 0) 263 releases := make([]*release.Release, 0) 264 var err error 265 ctx := context.Background() 266 267 matchLabels := metav1.LabelSelector{ 268 MatchExpressions: []metav1.LabelSelectorRequirement{ 269 { 270 Key: oam.LabelAppName, 271 Operator: metav1.LabelSelectorOpDoesNotExist, 272 }, 273 }, 274 } 275 selector, err := metav1.LabelSelectorAsSelector(&matchLabels) 276 if err != nil { 277 return err 278 } 279 280 switch opt.Type { 281 case adoptTypeNative: 282 for _, gvk := range opt.AllGVKs { 283 list := &unstructured.UnstructuredList{} 284 list.SetGroupVersionKind(gvk) 285 if err := f.Client().List(ctx, list, &client.ListOptions{Namespace: opt.AppNamespace, LabelSelector: selector}); err != nil { 286 apiVersion, kind := gvk.ToAPIVersionAndKind() 287 _, _ = fmt.Fprintf(opt.Out, "Warning: failed to list resources from %s/%s: %s\n", apiVersion, kind, err.Error()) 288 continue 289 } 290 dedup := make([]k8s.ResourceIdentifier, 0) 291 for _, item := range list.Items { 292 engine := resourcetopology.New(opt.ResourceTopologyRule) 293 itemIdentifier := k8s.ResourceIdentifier{ 294 Name: item.GetName(), 295 Namespace: item.GetNamespace(), 296 Kind: item.GetKind(), 297 APIVersion: item.GetAPIVersion(), 298 } 299 if velaslices.Contains(dedup, itemIdentifier) { 300 continue 301 } 302 firstElement := item 303 r := []*unstructured.Unstructured{&firstElement} 304 peers, err := engine.GetPeerResources(ctx, itemIdentifier) 305 if err != nil { 306 _, _ = fmt.Fprintf(opt.Out, "Warning: failed to get peer resources for %s/%s: %s\n", itemIdentifier.APIVersion, itemIdentifier.Kind, err.Error()) 307 resources = append(resources, r) 308 continue 309 } 310 dedup = append(dedup, peers...) 311 for _, peer := range peers { 312 gvk, err := k8s.GetGVKFromResource(peer) 313 if err != nil { 314 _, _ = fmt.Fprintf(opt.Out, "Warning: failed to get gvk from resource %s/%s: %s\n", peer.APIVersion, peer.Kind, err.Error()) 315 continue 316 } 317 peerResource := &unstructured.Unstructured{} 318 peerResource.SetGroupVersionKind(gvk) 319 if err := f.Client().Get(ctx, apitypes.NamespacedName{Namespace: peer.Namespace, Name: peer.Name}, peerResource); err != nil { 320 _, _ = fmt.Fprintf(opt.Out, "Warning: failed to get resource %s/%s: %s\n", peer.Namespace, peer.Name, err.Error()) 321 continue 322 } 323 r = append(r, peerResource) 324 } 325 resources = append(resources, r) 326 } 327 } 328 case adoptTypeHelm: 329 releases, err = opt.HelmConfig.Releases.List(func(release *release.Release) bool { 330 return true 331 }) 332 if err != nil { 333 return err 334 } 335 } 336 for _, r := range resources { 337 opt.Resources = r 338 opt.AppName = r[0].GetName() 339 opt.AppNamespace = r[0].GetNamespace() 340 if err := opt.Run(f, cmd); err != nil { 341 _, _ = fmt.Fprintf(opt.Out, "Error: failed to adopt %s/%s: %s", opt.AppNamespace, opt.AppName, err.Error()) 342 continue 343 } 344 } 345 for _, r := range releases { 346 opt.AppName = r.Name 347 opt.AppNamespace = r.Namespace 348 opt.HelmReleaseName = r.Name 349 opt.HelmReleaseNamespace = r.Namespace 350 // TODO(fog): filter the helm that already adopted by vela 351 if err := opt.loadHelm(); err != nil { 352 _, _ = fmt.Fprintf(opt.Out, "Error: failed to load helm for %s/%s: %s", opt.AppNamespace, opt.AppName, err.Error()) 353 continue 354 } 355 if err := opt.Run(f, cmd); err != nil { 356 _, _ = fmt.Fprintf(opt.Out, "Error: failed to adopt %s/%s: %s", opt.AppNamespace, opt.AppName, err.Error()) 357 continue 358 } 359 } 360 return nil 361 } 362 363 // Complete autofill fields in opts 364 func (opt *AdoptOptions) Complete(f velacmd.Factory, cmd *cobra.Command, args []string) (err error) { 365 opt.AppNamespace = velacmd.GetNamespace(f, cmd) 366 switch opt.Type { 367 case adoptTypeNative: 368 for _, arg := range args { 369 or, err := opt.parseResourceRef(f, cmd, arg) 370 if err != nil { 371 return err 372 } 373 opt.NativeResourceRefs = append(opt.NativeResourceRefs, or) 374 } 375 if opt.AppName == "" && velaslices.All(opt.NativeResourceRefs, func(ref *resourceRef) bool { 376 return ref.Name == opt.NativeResourceRefs[0].Name 377 }) { 378 opt.AppName = opt.NativeResourceRefs[0].Name 379 } 380 if opt.AppNamespace == "" { 381 opt.AppNamespace = opt.NativeResourceRefs[0].Namespace 382 } 383 if err := opt.loadNative(f, cmd); err != nil { 384 return err 385 } 386 case adoptTypeHelm: 387 if len(args) > 0 { 388 opt.HelmReleaseName = args[0] 389 } 390 if len(args) > 1 { 391 return fmt.Errorf("helm type adoption only support one helm release by far") 392 } 393 if opt.AppName == "" { 394 opt.AppName = opt.HelmReleaseName 395 } 396 if err := opt.loadHelm(); err != nil { 397 return err 398 } 399 default: 400 } 401 if opt.AppName != "" { 402 app := &v1beta1.Application{} 403 err := f.Client().Get(cmd.Context(), apitypes.NamespacedName{Namespace: opt.AppNamespace, Name: opt.AppName}, app) 404 if err == nil && app != nil { 405 if !opt.Yes && opt.Apply { 406 userInput := NewUserInput() 407 confirm := userInput.AskBool( 408 fmt.Sprintf("Application '%s' already exists, apply will override the existing app with the adopted one, please confirm [Y/n]: ", opt.AppName), 409 &UserInputOptions{AssumeYes: false}) 410 if !confirm { 411 return nil 412 } 413 } 414 } 415 } 416 opt.AdoptTemplateCUEValue, err = cuex.CompileString(cmd.Context(), fmt.Sprintf("%s\n\n%s: %s", opt.AdoptTemplate, adoptCUETempVal, adoptCUETempFunc)) 417 if err != nil { 418 return fmt.Errorf("failed to compile cue template: %w", err) 419 } 420 return err 421 } 422 423 // Validate if opts is valid 424 func (opt *AdoptOptions) Validate() error { 425 switch opt.Type { 426 case adoptTypeNative: 427 if len(opt.NativeResourceRefs) == 0 { 428 return fmt.Errorf("at least one resource should be specified") 429 } 430 if opt.AppName == "" { 431 return fmt.Errorf("app-name flag must be set for native resource adoption when multiple resources have different names") 432 } 433 case adoptTypeHelm: 434 if len(opt.HelmReleaseName) == 0 { 435 return fmt.Errorf("helm release name must not be empty") 436 } 437 } 438 return nil 439 } 440 441 func (opt *AdoptOptions) loadNative(f velacmd.Factory, cmd *cobra.Command) error { 442 for _, ref := range opt.NativeResourceRefs { 443 obj := &unstructured.Unstructured{} 444 obj.SetGroupVersionKind(ref.GroupVersionKind) 445 if err := f.Client().Get(multicluster.WithCluster(cmd.Context(), ref.Cluster), apitypes.NamespacedName{Namespace: ref.Namespace, Name: ref.Name}, obj); err != nil { 446 return fmt.Errorf("fail to get resource for %s: %w", ref.Arg, err) 447 } 448 _ = k8s.AddLabel(obj, oam.LabelAppCluster, ref.Cluster) 449 opt.Resources = append(opt.Resources, obj) 450 } 451 return nil 452 } 453 454 func (opt *AdoptOptions) loadHelm() error { 455 opt.HelmStore = opt.HelmConfig.Releases 456 revisions, err := opt.HelmStore.History(opt.HelmReleaseName) 457 if err != nil { 458 return fmt.Errorf("helm release %s/%s not loaded: %w", opt.HelmReleaseNamespace, opt.HelmReleaseName, err) 459 } 460 if len(revisions) == 0 { 461 return fmt.Errorf("helm release %s/%s not found", opt.HelmReleaseNamespace, opt.HelmReleaseName) 462 } 463 releaseutil.SortByRevision(revisions) 464 opt.HelmRelease = revisions[len(revisions)-1] 465 opt.HelmReleaseRevisions = revisions 466 manifests := releaseutil.SplitManifests(opt.HelmRelease.Manifest) 467 var objs []*unstructured.Unstructured 468 for _, val := range manifests { 469 obj := &unstructured.Unstructured{} 470 if err = yaml.Unmarshal([]byte(val), obj); err != nil { 471 klog.Warningf("unable to decode object %s: %s", val, err) 472 continue 473 } 474 _ = k8s.AddLabel(obj, oam.LabelAppCluster, multicluster.Local) 475 objs = append(objs, obj) 476 } 477 opt.Resources = objs 478 return nil 479 } 480 481 func (opt *AdoptOptions) render() (*v1beta1.Application, error) { 482 app := &v1beta1.Application{} 483 val := opt.AdoptTemplateCUEValue.FillPath(cue.ParsePath(adoptCUETempVal+".$args"), opt) 484 bs, err := val.LookupPath(cue.ParsePath(adoptCUETempVal + ".$returns")).MarshalJSON() 485 if err != nil { 486 return nil, fmt.Errorf("failed to parse adoption template: %w", err) 487 } 488 if err = json.Unmarshal(bs, app); err != nil { 489 return nil, fmt.Errorf("failed to parse template $returns into application: %w", err) 490 } 491 if app.Name == "" { 492 app.Name = opt.AppName 493 } 494 if app.Namespace == "" { 495 app.Namespace = opt.AppNamespace 496 } 497 return app, nil 498 } 499 500 // Run collect resources, assemble into application and print/apply 501 func (opt *AdoptOptions) Run(f velacmd.Factory, cmd *cobra.Command) error { 502 app, err := opt.render() 503 if err != nil { 504 return fmt.Errorf("failed to make adoption application for resources: %w", err) 505 } 506 if opt.Apply { 507 if err = apply.NewAPIApplicator(f.Client()).Apply(cmd.Context(), app); err != nil { 508 return fmt.Errorf("failed to apply application %s/%s: %w", app.Namespace, app.Name, err) 509 } 510 _, _ = fmt.Fprintf(opt.Out, "resources adopted in app %s/%s\n", app.Namespace, app.Name) 511 } else { 512 var bs []byte 513 if bs, err = yaml.Marshal(app); err != nil { 514 return fmt.Errorf("failed to encode application into YAML format: %w", err) 515 } 516 if opt.All { 517 _, _ = opt.Out.Write([]byte("\n---\n")) 518 } 519 _, _ = opt.Out.Write(bs) 520 } 521 if opt.Recycle && opt.Apply { 522 spinner := newTrackingSpinner("") 523 spinner.Writer = opt.Out 524 spinner.Start() 525 err = wait.PollImmediate(time.Second, time.Minute, func() (done bool, err error) { 526 _app := &v1beta1.Application{} 527 if err = f.Client().Get(cmd.Context(), client.ObjectKeyFromObject(app), _app); err != nil { 528 return false, err 529 } 530 spinner.UpdateCharSet([]string{fmt.Sprintf("waiting application %s/%s to be running, current status: %s", app.Namespace, app.Name, _app.Status.Phase)}) 531 return _app.Status.Phase == common.ApplicationRunning, nil 532 }) 533 spinner.Stop() 534 if err != nil { 535 return fmt.Errorf("failed to wait application %s/%s to be running: %w", app.Namespace, app.Name, err) 536 } 537 switch opt.Type { 538 case adoptTypeHelm: 539 for _, r := range opt.HelmReleaseRevisions { 540 if _, err = opt.HelmStore.Delete(r.Name, r.Version); err != nil { 541 return fmt.Errorf("failed to clean up helm release: %w", err) 542 } 543 } 544 _, _ = fmt.Fprintf(opt.Out, "successfully clean up old helm release\n") 545 default: 546 } 547 } 548 return nil 549 } 550 551 var ( 552 adoptLong = templates.LongDesc(i18n.T(` 553 Adopt resources into applications 554 555 Adopt resources into a KubeVela application. This command is useful when you already 556 have resources applied in your Kubernetes cluster. These resources could be applied 557 natively or with other tools, such as Helm. This command will automatically find out 558 the resources to be adopted and assemble them into a new application which won't 559 trigger any damage such as restart on the adoption. 560 561 There are two types of adoption supported by far, 'native' Kubernetes resources (by 562 default) and 'helm' releases. 563 1. For 'native' type, you can specify a list of resources you want to adopt in the 564 application. Only resources in local cluster are supported for now. 565 2. For 'helm' type, you can specify a helm release name. This helm release should 566 be already published in the local cluster. The command will find the resources 567 managed by the helm release and convert them into an adoption application. 568 569 There are two working mechanism (called 'modes' here) for the adoption by far, 570 'read-only' mode (by default) and 'take-over' mode. 571 1. In 'read-only' mode, adopted resources will not be touched. You can leverage vela 572 tools (like Vela CLI or VelaUX) to observe those resources and attach traits to add 573 new capabilities. The adopted resources will not be recycled or updated. This mode 574 is recommended if you still want to keep using other tools to manage resources updates 575 or deletion, like Helm. 576 2. In 'take-over' mode, adopted resources are completely managed by application which 577 means they can be modified. You can use traits or directly modify the component to make 578 edits to those resources. This mode can be helpful if you want to migrate existing 579 resources into KubeVela system and let KubeVela to handle the life-cycle of target 580 resources. 581 582 The adopted application can be customized. You can provide a CUE template file to 583 the command and make your own assemble rules for the adoption application. You can 584 refer to https://github.com/kubevela/kubevela/blob/master/references/cli/adopt-templates/default.cue 585 to see the default implementation of adoption rules. 586 587 If you want to adopt all resources with resource topology rule to Applications, 588 you can use: 'vela adopt --all'. The resource topology rule can be customized by 589 '--resource-topology-rule' flag. 590 `)) 591 adoptExample = templates.Examples(i18n.T(` 592 # Native Resources Adoption 593 594 ## Adopt resources into new application 595 596 ## Adopt all resources to Applications with resource topology rule 597 ## Use: vela adopt <resources-type> --all 598 vela adopt --all 599 vela adopt deployment --all --resource-topology-rule myrule.cue 600 601 ## Use: vela adopt <resources-type>[/<resource-cluster>][/<resource-namespace>]/<resource-name> <resources-type>[/<resource-cluster>][/<resource-namespace>]/<resource-name> ... 602 vela adopt deployment/my-app configmap/my-app 603 604 ## Adopt resources into new application with specified app name 605 vela adopt deployment/my-deploy configmap/my-config --app-name my-app 606 607 ## Adopt resources into new application in specified namespace 608 vela adopt deployment/my-app configmap/my-app -n demo 609 610 ## Adopt resources into new application across multiple namespace 611 vela adopt deployment/ns-1/my-app configmap/ns-2/my-app 612 613 ## Adopt resources into new application with take-over mode 614 vela adopt deployment/my-app configmap/my-app --mode take-over 615 616 ## Adopt resources into new application and apply it into cluster 617 vela adopt deployment/my-app configmap/my-app --apply 618 619 ----------------------------------------------------------- 620 621 # Helm Chart Adoption 622 623 ## Adopt all helm releases to Applications with resource topology rule 624 ## Use: vela adopt <resources-type> --all 625 vela adopt --all --type helm 626 vela adopt my-chart --all --resource-topology-rule myrule.cue --type helm 627 628 ## Adopt resources in a deployed helm chart 629 vela adopt my-chart -n my-namespace --type helm 630 631 ## Adopt resources in a deployed helm chart with take-over mode 632 vela adopt my-chart --type helm --mode take-over 633 634 ## Adopt resources in a deployed helm chart in an application and apply it into cluster 635 vela adopt my-chart --type helm --apply 636 637 ## Adopt resources in a deployed helm chart in an application, apply it into cluster, and recycle the old helm release after the adoption application successfully runs 638 vela adopt my-chart --type helm --apply --recycle 639 640 ----------------------------------------------------------- 641 642 ## Customize your adoption rules 643 vela adopt my-chart -n my-namespace --type helm --adopt-template my-rules.cue 644 `)) 645 ) 646 647 // NewAdoptCommand command for adopt resources into KubeVela Application 648 func NewAdoptCommand(f velacmd.Factory, order string, streams util.IOStreams) *cobra.Command { 649 o := &AdoptOptions{ 650 Type: adoptTypeNative, 651 Mode: adoptModeReadOnly, 652 IOStreams: streams, 653 } 654 cmd := &cobra.Command{ 655 Use: "adopt", 656 Short: i18n.T("Adopt resources into new application."), 657 Long: adoptLong, 658 Example: adoptExample, 659 Annotations: map[string]string{ 660 types.TagCommandType: types.TypeCD, 661 types.TagCommandOrder: order, 662 }, 663 Run: func(cmd *cobra.Command, args []string) { 664 cmdutil.CheckErr(o.Init(f, cmd, args)) 665 if o.All { 666 cmdutil.CheckErr(o.MultipleRun(f, cmd)) 667 return 668 } 669 cmdutil.CheckErr(o.Complete(f, cmd, args)) 670 cmdutil.CheckErr(o.Validate()) 671 cmdutil.CheckErr(o.Run(f, cmd)) 672 }, 673 } 674 cmd.Flags().StringVarP(&o.Type, "type", "t", o.Type, fmt.Sprintf("The type of adoption. Available values: [%s]", strings.Join(adoptTypes, ", "))) 675 cmd.Flags().StringVarP(&o.Mode, "mode", "m", o.Mode, fmt.Sprintf("The mode of adoption. Available values: [%s]", strings.Join(adoptModes, ", "))) 676 cmd.Flags().StringVarP(&o.AppName, "app-name", "", o.AppName, "The name of application for adoption. If empty for helm type adoption, it will inherit the helm chart's name.") 677 cmd.Flags().StringVarP(&o.AdoptTemplateFile, "adopt-template", "", o.AdoptTemplate, "The CUE template for adoption. If not provided, the default template will be used when --auto is switched on.") 678 cmd.Flags().StringVarP(&o.ResourceTopologyRuleFile, "resource-topology-rule", "", o.ResourceTopologyRule, "The CUE template for specify the rule of the resource topology. If not provided, the default rule will be used.") 679 cmd.Flags().StringVarP(&o.HelmDriver, "driver", "d", o.HelmDriver, "The storage backend of helm adoption. Only take effect when --type=helm.") 680 cmd.Flags().BoolVarP(&o.Apply, "apply", "", o.Apply, "If true, the application for adoption will be applied. Otherwise, it will only be printed.") 681 cmd.Flags().BoolVarP(&o.Recycle, "recycle", "", o.Recycle, "If true, when the adoption application is successfully applied, the old storage (like Helm secret) will be recycled.") 682 cmd.Flags().BoolVarP(&o.Yes, "yes", "y", o.Yes, "Skip confirmation prompt") 683 cmd.Flags().BoolVarP(&o.All, "all", "", o.All, "Adopt all resources in the namespace") 684 return velacmd.NewCommandBuilder(f, cmd). 685 WithNamespaceFlag(). 686 WithResponsiveWriter(). 687 Build() 688 }