2021-07-13 13:03:25 +08:00
|
|
|
/*
|
|
|
|
|
Copyright 2021 The KubeVela Authors.
|
|
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
|
limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
package cli
|
|
|
|
|
|
|
|
|
|
import (
|
2021-08-11 11:49:44 +08:00
|
|
|
"bytes"
|
2021-07-13 13:03:25 +08:00
|
|
|
"context"
|
|
|
|
|
"fmt"
|
2021-08-11 11:49:44 +08:00
|
|
|
"strings"
|
|
|
|
|
"text/template"
|
2021-07-13 13:03:25 +08:00
|
|
|
"time"
|
|
|
|
|
|
2021-08-17 14:46:07 +08:00
|
|
|
"github.com/Masterminds/sprig"
|
2021-07-13 13:03:25 +08:00
|
|
|
"github.com/gosuri/uitable"
|
2021-11-25 14:08:20 +08:00
|
|
|
terraformv1beta1 "github.com/oam-dev/terraform-controller/api/v1beta1"
|
2021-07-13 13:03:25 +08:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
|
v1 "k8s.io/api/core/v1"
|
2021-11-25 14:08:20 +08:00
|
|
|
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
2021-11-26 23:01:11 +08:00
|
|
|
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
2021-07-13 13:03:25 +08:00
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
|
|
|
types2 "k8s.io/apimachinery/pkg/types"
|
2021-11-25 14:08:20 +08:00
|
|
|
yaml2 "k8s.io/apimachinery/pkg/util/yaml"
|
2021-07-13 13:03:25 +08:00
|
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
|
|
2021-11-25 14:08:20 +08:00
|
|
|
common2 "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
2021-07-13 13:03:25 +08:00
|
|
|
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
|
|
|
|
"github.com/oam-dev/kubevela/apis/types"
|
2021-09-14 19:35:21 +08:00
|
|
|
"github.com/oam-dev/kubevela/pkg/oam"
|
2021-11-25 14:08:20 +08:00
|
|
|
"github.com/oam-dev/kubevela/pkg/oam/util"
|
2021-07-13 13:03:25 +08:00
|
|
|
"github.com/oam-dev/kubevela/pkg/utils/apply"
|
|
|
|
|
"github.com/oam-dev/kubevela/pkg/utils/common"
|
|
|
|
|
cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
// DescAnnotation records the description of addon
|
|
|
|
|
DescAnnotation = "addons.oam.dev/description"
|
2021-11-16 13:56:39 +08:00
|
|
|
|
|
|
|
|
// DependsOnWorkFlowStepName is workflow step name which is used to check dependsOn app
|
|
|
|
|
DependsOnWorkFlowStepName = "depends-on-app"
|
2021-11-25 10:32:30 +08:00
|
|
|
|
|
|
|
|
// AddonTerraformProviderNamespace is the namespace of addon terraform provider
|
|
|
|
|
AddonTerraformProviderNamespace = "default"
|
|
|
|
|
// AddonTerraformProviderNameArgument is the argument name of addon terraform provider
|
|
|
|
|
AddonTerraformProviderNameArgument = "providerName"
|
2021-07-13 13:03:25 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var statusUninstalled = "uninstalled"
|
|
|
|
|
var statusInstalled = "installed"
|
|
|
|
|
var clt client.Client
|
|
|
|
|
var clientArgs common.Args
|
|
|
|
|
|
2021-10-14 18:21:49 +08:00
|
|
|
var legacyAddonNamespace map[string]string
|
|
|
|
|
|
2021-07-13 13:03:25 +08:00
|
|
|
func init() {
|
|
|
|
|
clientArgs, _ = common.InitBaseRestConfig()
|
|
|
|
|
clt, _ = clientArgs.GetClient()
|
2021-10-14 18:21:49 +08:00
|
|
|
legacyAddonNamespace = map[string]string{
|
2021-11-23 00:48:49 +08:00
|
|
|
"fluxcd": types.DefaultKubeVelaNS,
|
|
|
|
|
"ns-flux-system": types.DefaultKubeVelaNS,
|
|
|
|
|
"kruise": types.DefaultKubeVelaNS,
|
|
|
|
|
"prometheus": types.DefaultKubeVelaNS,
|
|
|
|
|
"observability": "observability",
|
|
|
|
|
"observability-asset": types.DefaultKubeVelaNS,
|
|
|
|
|
"istio": "istio-system",
|
|
|
|
|
"ns-istio-system": types.DefaultKubeVelaNS,
|
|
|
|
|
"keda": types.DefaultKubeVelaNS,
|
|
|
|
|
"ocm-cluster-manager": types.DefaultKubeVelaNS,
|
|
|
|
|
"terraform": types.DefaultKubeVelaNS,
|
|
|
|
|
"terraform-provider/alibaba": "default",
|
|
|
|
|
"terraform-provider/azure": "default",
|
2021-10-14 18:21:49 +08:00
|
|
|
}
|
2021-07-13 13:03:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewAddonCommand create `addon` command
|
|
|
|
|
func NewAddonCommand(c common.Args, ioStreams cmdutil.IOStreams) *cobra.Command {
|
|
|
|
|
cmd := &cobra.Command{
|
|
|
|
|
Use: "addon",
|
|
|
|
|
Short: "List and get addon in KubeVela",
|
|
|
|
|
Long: "List and get addon in KubeVela",
|
|
|
|
|
Annotations: map[string]string{
|
|
|
|
|
types.TagCommandType: types.TypeSystem,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
cmd.AddCommand(
|
|
|
|
|
NewAddonListCommand(),
|
2021-11-25 10:32:30 +08:00
|
|
|
NewAddonEnableCommand(c, ioStreams),
|
2021-07-13 13:03:25 +08:00
|
|
|
NewAddonDisableCommand(ioStreams),
|
|
|
|
|
)
|
|
|
|
|
return cmd
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewAddonListCommand create addon list command
|
|
|
|
|
func NewAddonListCommand() *cobra.Command {
|
|
|
|
|
return &cobra.Command{
|
2021-10-14 18:21:49 +08:00
|
|
|
Use: "list",
|
|
|
|
|
Aliases: []string{"ls"},
|
|
|
|
|
Short: "List addons",
|
|
|
|
|
Long: "List addons in KubeVela",
|
2021-07-13 13:03:25 +08:00
|
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
|
|
|
err := listAddons()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewAddonEnableCommand create addon enable command
|
2021-11-25 10:32:30 +08:00
|
|
|
func NewAddonEnableCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Command {
|
|
|
|
|
ctx := context.Background()
|
2021-07-13 13:03:25 +08:00
|
|
|
return &cobra.Command{
|
|
|
|
|
Use: "enable",
|
|
|
|
|
Short: "enable an addon",
|
|
|
|
|
Long: "enable an addon in cluster",
|
|
|
|
|
Example: "vela addon enable <addon-name>",
|
|
|
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
2021-11-25 10:32:30 +08:00
|
|
|
k8sClient, err := c.GetClient()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-13 13:03:25 +08:00
|
|
|
if len(args) < 1 {
|
2021-08-18 11:37:46 +08:00
|
|
|
return fmt.Errorf("must specify addon name")
|
2021-07-13 13:03:25 +08:00
|
|
|
}
|
|
|
|
|
name := args[0]
|
2021-08-11 11:49:44 +08:00
|
|
|
addonArgs, err := parseToMap(args[1:])
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2021-11-25 10:32:30 +08:00
|
|
|
err = enableAddon(ctx, k8sClient, name, addonArgs)
|
2021-07-13 13:03:25 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2021-10-14 18:21:49 +08:00
|
|
|
fmt.Printf("Successfully enable addon:%s\n", name)
|
2021-07-13 13:03:25 +08:00
|
|
|
return nil
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-11 11:49:44 +08:00
|
|
|
func parseToMap(args []string) (map[string]string, error) {
|
|
|
|
|
res := map[string]string{}
|
|
|
|
|
for _, pair := range args {
|
|
|
|
|
line := strings.Split(pair, "=")
|
|
|
|
|
if len(line) != 2 {
|
|
|
|
|
return nil, fmt.Errorf("parameter format should be foo=bar, %s not match", pair)
|
|
|
|
|
}
|
|
|
|
|
k := strings.TrimSpace(line[0])
|
|
|
|
|
v := strings.TrimSpace(line[1])
|
|
|
|
|
if k != "" && v != "" {
|
|
|
|
|
res[k] = v
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return res, nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-13 13:03:25 +08:00
|
|
|
// NewAddonDisableCommand create addon disable command
|
|
|
|
|
func NewAddonDisableCommand(ioStream cmdutil.IOStreams) *cobra.Command {
|
|
|
|
|
return &cobra.Command{
|
|
|
|
|
Use: "disable",
|
|
|
|
|
Short: "disable an addon",
|
|
|
|
|
Long: "disable an addon in cluster",
|
|
|
|
|
Example: "vela addon disable <addon-name>",
|
|
|
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
|
|
|
if len(args) < 1 {
|
2021-08-18 11:37:46 +08:00
|
|
|
return fmt.Errorf("must specify addon name")
|
2021-07-13 13:03:25 +08:00
|
|
|
}
|
|
|
|
|
name := args[0]
|
|
|
|
|
err := disableAddon(name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2021-10-14 18:21:49 +08:00
|
|
|
fmt.Printf("Successfully disable addon:%s\n", name)
|
2021-07-13 13:03:25 +08:00
|
|
|
return nil
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func listAddons() error {
|
|
|
|
|
repo, err := NewAddonRepo()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
addons := repo.listAddons()
|
|
|
|
|
table := uitable.New()
|
2021-10-14 18:21:49 +08:00
|
|
|
table.AddRow("NAME", "DESCRIPTION", "STATUS")
|
2021-07-13 13:03:25 +08:00
|
|
|
for _, addon := range addons {
|
2021-11-26 23:01:11 +08:00
|
|
|
// Addon terraform should be invisible to end-users. It will be installed by other addons like `terraform-alibaba`
|
|
|
|
|
if addon.name == "terraform" {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2021-10-14 18:21:49 +08:00
|
|
|
table.AddRow(addon.name, addon.description, addon.getStatus())
|
2021-07-13 13:03:25 +08:00
|
|
|
}
|
|
|
|
|
fmt.Println(table.String())
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-25 10:32:30 +08:00
|
|
|
func enableAddon(ctx context.Context, k8sClient client.Client, name string, args map[string]string) error {
|
2021-07-13 13:03:25 +08:00
|
|
|
repo, err := NewAddonRepo()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2021-11-25 10:32:30 +08:00
|
|
|
|
2021-07-13 13:03:25 +08:00
|
|
|
addon, err := repo.getAddon(name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2021-11-26 23:01:11 +08:00
|
|
|
if strings.HasPrefix(name, "terraform-") {
|
2021-11-25 10:32:30 +08:00
|
|
|
args, _ = getTerraformProviderArgumentValue(name, args)
|
|
|
|
|
}
|
2021-08-11 11:49:44 +08:00
|
|
|
addon.setArgs(args)
|
2021-11-25 10:32:30 +08:00
|
|
|
err = addon.enable(ctx, k8sClient, name, args)
|
2021-10-14 18:21:49 +08:00
|
|
|
return err
|
2021-07-13 13:03:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func disableAddon(name string) error {
|
2021-10-14 18:21:49 +08:00
|
|
|
if isLegacyAddonExist(name) {
|
|
|
|
|
return tryDisableInitializerAddon(name)
|
|
|
|
|
}
|
2021-07-13 13:03:25 +08:00
|
|
|
repo, err := NewAddonRepo()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
addon, err := repo.getAddon(name)
|
|
|
|
|
if err != nil {
|
2021-10-14 18:21:49 +08:00
|
|
|
return errors.Wrap(err, "get addon err")
|
2021-07-13 13:03:25 +08:00
|
|
|
}
|
|
|
|
|
if addon.getStatus() == statusUninstalled {
|
|
|
|
|
fmt.Printf("Addon %s is not installed\n", addon.name)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2021-10-14 18:21:49 +08:00
|
|
|
return addon.disable()
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func isLegacyAddonExist(name string) bool {
|
|
|
|
|
if namespace, ok := legacyAddonNamespace[name]; ok {
|
|
|
|
|
convertedAddonName := TransAddonName(name)
|
|
|
|
|
init := unstructured.Unstructured{
|
|
|
|
|
Object: map[string]interface{}{
|
|
|
|
|
"apiVersion": "core.oam.dev/v1beta1",
|
|
|
|
|
"kind": "Initializer",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
err := clt.Get(context.TODO(), client.ObjectKey{
|
|
|
|
|
Namespace: namespace,
|
|
|
|
|
Name: convertedAddonName,
|
|
|
|
|
}, &init)
|
|
|
|
|
return err == nil
|
2021-07-13 13:03:25 +08:00
|
|
|
}
|
2021-10-14 18:21:49 +08:00
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func tryDisableInitializerAddon(addonName string) error {
|
|
|
|
|
fmt.Printf("Trying to disable addon in initializer implementation...\n")
|
|
|
|
|
init := unstructured.Unstructured{
|
|
|
|
|
Object: map[string]interface{}{
|
|
|
|
|
"apiVersion": "core.oam.dev/v1beta1",
|
|
|
|
|
"kind": "Initializer",
|
|
|
|
|
"metadata": map[string]interface{}{
|
|
|
|
|
"name": TransAddonName(addonName),
|
|
|
|
|
"namespace": legacyAddonNamespace[addonName],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
return clt.Delete(context.TODO(), &init)
|
|
|
|
|
|
2021-07-13 13:03:25 +08:00
|
|
|
}
|
|
|
|
|
func newAddon(data *v1.ConfigMap) *Addon {
|
|
|
|
|
description := data.ObjectMeta.Annotations[DescAnnotation]
|
2021-10-14 18:21:49 +08:00
|
|
|
a := Addon{name: data.Annotations[oam.AnnotationAddonsName], description: description, data: data.Data["application"]}
|
2021-07-13 13:03:25 +08:00
|
|
|
return &a
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AddonRepo is a place to store addon info
|
|
|
|
|
type AddonRepo interface {
|
|
|
|
|
getAddon(name string) (Addon, error)
|
|
|
|
|
listAddons() []Addon
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewAddonRepo create new addon repo,now only support ConfigMap
|
|
|
|
|
func NewAddonRepo() (AddonRepo, error) {
|
|
|
|
|
list := v1.ConfigMapList{}
|
|
|
|
|
matchLabels := metav1.LabelSelector{
|
|
|
|
|
MatchExpressions: []metav1.LabelSelectorRequirement{{
|
2021-09-14 19:35:21 +08:00
|
|
|
Key: oam.LabelAddonsName,
|
2021-07-13 13:03:25 +08:00
|
|
|
Operator: metav1.LabelSelectorOpExists,
|
|
|
|
|
}},
|
|
|
|
|
}
|
|
|
|
|
selector, err := metav1.LabelSelectorAsSelector(&matchLabels)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
err = clt.List(context.Background(), &list, &client.ListOptions{LabelSelector: selector})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "Get addon list failed")
|
|
|
|
|
}
|
|
|
|
|
return configMapAddonRepo{maps: list.Items}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type configMapAddonRepo struct {
|
|
|
|
|
maps []v1.ConfigMap
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-14 18:21:49 +08:00
|
|
|
// AddonNotFoundErr means addon not found
|
|
|
|
|
type AddonNotFoundErr struct {
|
|
|
|
|
addonName string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (e AddonNotFoundErr) Error() string {
|
|
|
|
|
return fmt.Sprintf("addon %s not found", e.addonName)
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-13 13:03:25 +08:00
|
|
|
func (c configMapAddonRepo) getAddon(name string) (Addon, error) {
|
|
|
|
|
for i := range c.maps {
|
2021-09-14 19:35:21 +08:00
|
|
|
if addonName, ok := c.maps[i].Annotations[oam.AnnotationAddonsName]; ok && name == addonName {
|
2021-07-13 13:03:25 +08:00
|
|
|
return *newAddon(&c.maps[i]), nil
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-10-14 18:21:49 +08:00
|
|
|
return Addon{}, AddonNotFoundErr{addonName: name}
|
2021-07-13 13:03:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c configMapAddonRepo) listAddons() []Addon {
|
|
|
|
|
var addons []Addon
|
|
|
|
|
for i := range c.maps {
|
|
|
|
|
addon := newAddon(&c.maps[i])
|
|
|
|
|
addons = append(addons, *addon)
|
|
|
|
|
}
|
|
|
|
|
return addons
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Addon consist of a Initializer resource to enable an addon
|
|
|
|
|
type Addon struct {
|
2021-10-14 18:21:49 +08:00
|
|
|
name string
|
|
|
|
|
description string
|
|
|
|
|
data string
|
2021-08-11 11:49:44 +08:00
|
|
|
// Args is map for renderInitializer
|
|
|
|
|
Args map[string]string
|
2021-11-16 13:56:39 +08:00
|
|
|
application *v1beta1.Application
|
2021-07-13 13:03:25 +08:00
|
|
|
}
|
|
|
|
|
|
2021-11-16 13:56:39 +08:00
|
|
|
func (a *Addon) renderApplication() (*v1beta1.Application, error) {
|
2021-08-11 11:49:44 +08:00
|
|
|
if a.Args == nil {
|
|
|
|
|
a.Args = map[string]string{}
|
2021-07-13 13:03:25 +08:00
|
|
|
}
|
2021-10-14 18:21:49 +08:00
|
|
|
t, err := template.New("addon-template").Delims("[[", "]]").Funcs(sprig.TxtFuncMap()).Parse(a.data)
|
2021-08-11 11:49:44 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "parsing addon initializer template error")
|
|
|
|
|
}
|
|
|
|
|
buf := bytes.Buffer{}
|
|
|
|
|
err = t.Execute(&buf, a)
|
|
|
|
|
if err != nil {
|
2021-10-14 18:21:49 +08:00
|
|
|
return nil, errors.Wrap(err, "application template render fail")
|
2021-08-11 11:49:44 +08:00
|
|
|
}
|
2021-11-16 13:56:39 +08:00
|
|
|
err = yaml2.NewYAMLOrJSONDecoder(&buf, buf.Len()).Decode(&a.application)
|
2021-08-11 11:49:44 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2021-10-14 18:21:49 +08:00
|
|
|
return a.application, nil
|
2021-07-13 13:03:25 +08:00
|
|
|
}
|
|
|
|
|
|
2021-11-25 10:32:30 +08:00
|
|
|
func (a *Addon) enable(ctx context.Context, k8sClient client.Client, name string, args map[string]string) error {
|
2021-07-13 13:03:25 +08:00
|
|
|
applicator := apply.NewAPIApplicator(clt)
|
2021-10-14 18:21:49 +08:00
|
|
|
obj, err := a.renderApplication()
|
2021-07-13 13:03:25 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2021-11-25 10:32:30 +08:00
|
|
|
|
2021-11-26 23:01:11 +08:00
|
|
|
if strings.HasPrefix(name, "terraform-") {
|
2021-11-25 10:32:30 +08:00
|
|
|
providerName, existed, err := checkWhetherTerraformProviderExist(ctx, k8sClient, name, args)
|
2021-11-26 23:01:11 +08:00
|
|
|
if err != nil && !apimeta.IsNoMatchError(err) {
|
2021-11-25 10:32:30 +08:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if existed {
|
|
|
|
|
return errors.Errorf("terraform provider %s with name %s already exists", name, providerName)
|
|
|
|
|
}
|
|
|
|
|
obj.Name = fmt.Sprintf("%s-%s", obj.Name, providerName)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = a.installDependsOn(ctx, k8sClient, args)
|
2021-11-16 13:56:39 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrap(err, "Error occurs when install dependent addon")
|
|
|
|
|
}
|
2021-07-13 13:03:25 +08:00
|
|
|
err = applicator.Apply(ctx, obj)
|
|
|
|
|
if err != nil {
|
2021-10-14 18:21:49 +08:00
|
|
|
return errors.Wrapf(err, "Error occurs when apply addon application: %s\n", a.name)
|
2021-07-13 13:03:25 +08:00
|
|
|
}
|
2021-10-14 18:21:49 +08:00
|
|
|
err = waitApplicationRunning(a.application)
|
2021-07-25 10:16:43 +08:00
|
|
|
if err != nil {
|
2021-10-14 18:21:49 +08:00
|
|
|
return errors.Wrap(err, "Error occurs when waiting addon applicatoin running")
|
2021-07-25 10:16:43 +08:00
|
|
|
}
|
2021-07-13 13:03:25 +08:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-16 13:56:39 +08:00
|
|
|
func waitApplicationRunning(obj *v1beta1.Application) error {
|
2021-11-25 14:08:20 +08:00
|
|
|
trackInterval := 5 * time.Second
|
2021-07-30 10:02:51 +08:00
|
|
|
timeout := 10 * time.Minute
|
2021-11-25 14:08:20 +08:00
|
|
|
start := time.Now()
|
|
|
|
|
ctx := context.Background()
|
2021-10-14 18:21:49 +08:00
|
|
|
var app v1beta1.Application
|
2021-11-25 14:08:20 +08:00
|
|
|
spinner := newTrackingSpinnerWithDelay("Waiting addon running ...", 1*time.Second)
|
|
|
|
|
spinner.Start()
|
|
|
|
|
defer spinner.Stop()
|
|
|
|
|
|
|
|
|
|
for {
|
|
|
|
|
err := clt.Get(ctx, types2.NamespacedName{Name: obj.GetName(), Namespace: obj.GetNamespace()}, &app)
|
2021-07-25 10:16:43 +08:00
|
|
|
if err != nil {
|
2021-11-25 14:08:20 +08:00
|
|
|
return client.IgnoreNotFound(err)
|
2021-07-25 10:16:43 +08:00
|
|
|
}
|
2021-10-14 18:21:49 +08:00
|
|
|
phase := app.Status.Phase
|
|
|
|
|
if phase == common2.ApplicationRunning {
|
2021-11-25 14:08:20 +08:00
|
|
|
return nil
|
2021-07-25 10:16:43 +08:00
|
|
|
}
|
2021-11-25 14:08:20 +08:00
|
|
|
timeConsumed := int(time.Since(start).Seconds())
|
|
|
|
|
applySpinnerNewSuffix(spinner, fmt.Sprintf("Waiting addon application running. It is now in phase: %s (timeout %d/%d seconds)...",
|
|
|
|
|
phase, timeConsumed, int(timeout.Seconds())))
|
|
|
|
|
time.Sleep(trackInterval)
|
|
|
|
|
}
|
2021-07-25 10:16:43 +08:00
|
|
|
}
|
2021-07-13 13:03:25 +08:00
|
|
|
func (a *Addon) disable() error {
|
2021-10-14 18:21:49 +08:00
|
|
|
obj, err := a.renderApplication()
|
2021-07-13 13:03:25 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
fmt.Println("Deleting all resources...")
|
2021-11-16 13:56:39 +08:00
|
|
|
err = clt.Delete(context.TODO(), obj, client.PropagationPolicy(metav1.DeletePropagationForeground))
|
2021-07-13 13:03:25 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *Addon) getStatus() string {
|
2021-10-14 18:21:49 +08:00
|
|
|
var application v1beta1.Application
|
2021-07-13 13:03:25 +08:00
|
|
|
err := clt.Get(context.Background(), client.ObjectKey{
|
2021-10-14 18:21:49 +08:00
|
|
|
Namespace: types.DefaultKubeVelaNS,
|
2021-09-14 19:35:21 +08:00
|
|
|
Name: TransAddonName(a.name),
|
2021-10-14 18:21:49 +08:00
|
|
|
}, &application)
|
2021-07-13 13:03:25 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return statusUninstalled
|
|
|
|
|
}
|
|
|
|
|
return statusInstalled
|
|
|
|
|
}
|
2021-08-11 11:49:44 +08:00
|
|
|
|
|
|
|
|
func (a *Addon) setArgs(args map[string]string) {
|
|
|
|
|
a.Args = args
|
|
|
|
|
}
|
2021-09-14 19:35:21 +08:00
|
|
|
|
2021-11-25 10:32:30 +08:00
|
|
|
func (a *Addon) installDependsOn(ctx context.Context, k8sClient client.Client, args map[string]string) error {
|
2021-11-16 13:56:39 +08:00
|
|
|
if a.application.Spec.Workflow == nil || a.application.Spec.Workflow.Steps == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
repo, err := NewAddonRepo()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
for _, step := range a.application.Spec.Workflow.Steps {
|
|
|
|
|
if step.Type == DependsOnWorkFlowStepName {
|
|
|
|
|
props, err := util.RawExtension2Map(step.Properties)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
dependsOnAddonName, _ := props["name"].(string)
|
|
|
|
|
fmt.Printf("Installing dependent addon: %s\n", dependsOnAddonName)
|
|
|
|
|
addon, err := repo.getAddon(dependsOnAddonName)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if addon.getStatus() != statusInstalled {
|
2021-11-25 10:32:30 +08:00
|
|
|
err = addon.enable(ctx, k8sClient, dependsOnAddonName, args)
|
2021-11-16 13:56:39 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-14 19:35:21 +08:00
|
|
|
// TransAddonName will turn addon's name from xxx/yyy to xxx-yyy
|
|
|
|
|
func TransAddonName(name string) string {
|
|
|
|
|
return strings.ReplaceAll(name, "/", "-")
|
|
|
|
|
}
|
2021-11-25 10:32:30 +08:00
|
|
|
|
|
|
|
|
func getTerraformProviderNames(ctx context.Context, k8sClient client.Client) ([]string, error) {
|
|
|
|
|
var names []string
|
|
|
|
|
providerList := &terraformv1beta1.ProviderList{}
|
|
|
|
|
err := k8sClient.List(ctx, providerList, client.InNamespace(AddonTerraformProviderNamespace))
|
|
|
|
|
if err != nil {
|
2021-11-26 23:01:11 +08:00
|
|
|
if apimeta.IsNoMatchError(err) || kerrors.IsNotFound(err) {
|
|
|
|
|
return nil, nil
|
2021-11-25 10:32:30 +08:00
|
|
|
}
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
for _, provider := range providerList.Items {
|
|
|
|
|
names = append(names, provider.Name)
|
|
|
|
|
}
|
|
|
|
|
return names, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get the value of argument AddonTerraformProviderNameArgument
|
|
|
|
|
func getTerraformProviderArgumentValue(addonName string, args map[string]string) (map[string]string, string) {
|
|
|
|
|
providerName, ok := args[AddonTerraformProviderNameArgument]
|
|
|
|
|
if !ok {
|
|
|
|
|
switch addonName {
|
2021-11-26 23:01:11 +08:00
|
|
|
case "terraform-alibaba":
|
2021-11-25 10:32:30 +08:00
|
|
|
providerName = "default"
|
2021-11-26 23:01:11 +08:00
|
|
|
case "terraform-aws":
|
2021-11-25 10:32:30 +08:00
|
|
|
providerName = "aws"
|
2021-11-26 23:01:11 +08:00
|
|
|
case "terraform-azure":
|
2021-11-25 10:32:30 +08:00
|
|
|
providerName = "azure"
|
|
|
|
|
}
|
|
|
|
|
args[AddonTerraformProviderNameArgument] = providerName
|
|
|
|
|
}
|
|
|
|
|
return args, providerName
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func checkWhetherTerraformProviderExist(ctx context.Context, k8sClient client.Client, addonName string, args map[string]string) (string, bool, error) {
|
|
|
|
|
_, providerName := getTerraformProviderArgumentValue(addonName, args)
|
|
|
|
|
|
|
|
|
|
providerNames, err := getTerraformProviderNames(ctx, k8sClient)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", false, err
|
|
|
|
|
}
|
|
|
|
|
for _, name := range providerNames {
|
|
|
|
|
if providerName == name {
|
|
|
|
|
return providerName, true, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return providerName, false, nil
|
|
|
|
|
}
|