mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-14 18:10:21 +00:00
351 lines
11 KiB
Go
351 lines
11 KiB
Go
package commands
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/openservicemesh/osm/pkg/cli"
|
|
"github.com/pkg/errors"
|
|
"github.com/spf13/cobra"
|
|
"helm.sh/helm/v3/pkg/chart"
|
|
"helm.sh/helm/v3/pkg/chart/loader"
|
|
"helm.sh/helm/v3/pkg/strvals"
|
|
corev1 "k8s.io/api/core/v1"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
"github.com/oam-dev/kubevela/apis/types"
|
|
cmdutil "github.com/oam-dev/kubevela/pkg/commands/util"
|
|
"github.com/oam-dev/kubevela/pkg/plugins"
|
|
"github.com/oam-dev/kubevela/pkg/utils/helm"
|
|
)
|
|
|
|
// VelaRuntimeStatus enums vela-core runtime status
|
|
type VelaRuntimeStatus int
|
|
|
|
// Enums of VelaRuntimeStatus
|
|
const (
|
|
NotFound VelaRuntimeStatus = iota
|
|
Pending
|
|
Ready
|
|
Error
|
|
)
|
|
|
|
type initCmd struct {
|
|
namespace string
|
|
ioStreams cmdutil.IOStreams
|
|
client client.Client
|
|
chartPath string
|
|
chartArgs chartArgs
|
|
waitReady string
|
|
c types.Args
|
|
}
|
|
|
|
type chartArgs struct {
|
|
imageRepo string
|
|
imageTag string
|
|
imagePullPolicy string
|
|
}
|
|
|
|
type infoCmd struct {
|
|
out io.Writer
|
|
}
|
|
|
|
// SystemCommandGroup creates `system` command and its nested children command
|
|
func SystemCommandGroup(c types.Args, ioStream cmdutil.IOStreams) *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "system",
|
|
Short: "System management utilities",
|
|
Long: "System management utilities",
|
|
Annotations: map[string]string{
|
|
types.TagCommandType: types.TypeSystem,
|
|
},
|
|
}
|
|
cmd.AddCommand(NewAdminInfoCommand(ioStream))
|
|
return cmd
|
|
}
|
|
|
|
// NewAdminInfoCommand creates `system info` command
|
|
func NewAdminInfoCommand(ioStreams cmdutil.IOStreams) *cobra.Command {
|
|
i := &infoCmd{out: ioStreams.Out}
|
|
|
|
cmd := &cobra.Command{
|
|
Use: "info",
|
|
Short: "Show vela client and cluster chartPath",
|
|
Long: "Show vela client and cluster chartPath",
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
return i.run(ioStreams)
|
|
},
|
|
Annotations: map[string]string{
|
|
types.TagCommandType: types.TypeSystem,
|
|
},
|
|
}
|
|
return cmd
|
|
}
|
|
|
|
func (i *infoCmd) run(ioStreams cmdutil.IOStreams) error {
|
|
clusterVersion, err := GetOAMReleaseVersion(types.DefaultKubeVelaNS)
|
|
if err != nil {
|
|
return fmt.Errorf("fail to get cluster chartPath: %w", err)
|
|
}
|
|
ioStreams.Info("Versions:")
|
|
ioStreams.Infof("oam-kubernetes-runtime: %s \n", clusterVersion)
|
|
// TODO(wonderflow): we should print all helm charts installed by vela, including plugins
|
|
|
|
return nil
|
|
}
|
|
|
|
// NewInstallCommand creates `install` command
|
|
func NewInstallCommand(c types.Args, chartContent string, ioStreams cmdutil.IOStreams) *cobra.Command {
|
|
i := &initCmd{ioStreams: ioStreams}
|
|
cmd := &cobra.Command{
|
|
Use: "install",
|
|
Short: "Install Vela Core with built-in capabilities",
|
|
Long: "Install Vela Core with built-in capabilities",
|
|
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
|
return c.SetConfig()
|
|
},
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
i.client = newClient
|
|
i.namespace = types.DefaultKubeVelaNS
|
|
i.c = c
|
|
return i.run(ioStreams, chartContent)
|
|
},
|
|
Annotations: map[string]string{
|
|
types.TagCommandType: types.TypeStart,
|
|
},
|
|
}
|
|
|
|
flag := cmd.Flags()
|
|
flag.StringVarP(&i.chartPath, "vela-chart-path", "p", "", "path to vela core chart to override default chart")
|
|
flag.StringVarP(&i.chartArgs.imagePullPolicy, "image-pull-policy", "", "", "vela core image pull policy, this will align to chart value image.pullPolicy")
|
|
flag.StringVarP(&i.chartArgs.imageRepo, "image-repo", "", "", "vela core image repo, this will align to chart value image.repo")
|
|
flag.StringVarP(&i.chartArgs.imageTag, "image-tag", "", "", "vela core image repo, this will align to chart value image.tag")
|
|
flag.StringVarP(&i.waitReady, "wait", "w", "0s", "wait until vela-core is ready to serve, default will not wait")
|
|
|
|
return cmd
|
|
}
|
|
|
|
func (i *initCmd) run(ioStreams cmdutil.IOStreams, chartSource string) error {
|
|
waitDuration, err := time.ParseDuration(i.waitReady)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid wait timeoout duration %w, should use '120s', '5m' like format", err)
|
|
}
|
|
|
|
ioStreams.Info("- Installing Vela Core Chart:")
|
|
exist, err := cmdutil.DoesNamespaceExist(i.client, types.DefaultKubeVelaNS)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !exist {
|
|
if err := cmdutil.NewNamespace(i.client, types.DefaultKubeVelaNS); err != nil {
|
|
return err
|
|
}
|
|
ioStreams.Info("created namespace", types.DefaultKubeVelaNS)
|
|
}
|
|
|
|
if helm.IsHelmReleaseRunning(types.DefaultKubeVelaReleaseName, types.DefaultKubeVelaChartName, types.DefaultKubeVelaNS, i.ioStreams) {
|
|
i.ioStreams.Info("Vela system along with OAM runtime already exist.")
|
|
} else {
|
|
vals, err := i.resolveValues()
|
|
if err != nil {
|
|
i.ioStreams.Errorf("resolve values for vela-core chart err %v, will install with default values", err)
|
|
vals = make(map[string]interface{})
|
|
}
|
|
if err := InstallOamRuntime(i.chartPath, chartSource, vals, ioStreams); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if err = CheckCapabilityReady(context.Background(), i.c, waitDuration); err != nil {
|
|
ioStreams.Infof("- Vela-Core was installed successfully while some capabilities were still installing background, "+
|
|
"try running 'vela workloads' or 'vela traits' to check after a while, details %v", err)
|
|
return nil
|
|
}
|
|
if err := RefreshDefinitions(context.Background(), i.c, ioStreams, false, true); err != nil {
|
|
return err
|
|
}
|
|
ioStreams.Info("- Finished successfully.")
|
|
|
|
if waitDuration > 0 {
|
|
_, err := PrintTrackVelaRuntimeStatus(context.Background(), i.client, ioStreams, waitDuration)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CheckCapabilityReady waits unitl capability is installed successfully
|
|
func CheckCapabilityReady(ctx context.Context, c types.Args, timeout time.Duration) error {
|
|
if timeout < 2*time.Minute {
|
|
timeout = 2 * time.Minute
|
|
}
|
|
tmpdir, err := ioutil.TempDir(".", "tmpcap")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
//nolint:errcheck
|
|
defer os.RemoveAll(tmpdir)
|
|
|
|
start := time.Now()
|
|
spiner := newTrackingSpinner("Waiting Capability ready to install ...")
|
|
spiner.Start()
|
|
defer spiner.Stop()
|
|
|
|
for {
|
|
_, err = plugins.GetCapabilitiesFromCluster(ctx, types.DefaultKubeVelaNS, c, tmpdir, nil)
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
if time.Since(start) > timeout {
|
|
return fmt.Errorf("timeout checking capability ready: %w", err)
|
|
}
|
|
time.Sleep(5 * time.Second)
|
|
}
|
|
}
|
|
|
|
func (i *initCmd) resolveValues() (map[string]interface{}, error) {
|
|
finalValues := map[string]interface{}{}
|
|
var valuesConfig []string
|
|
// By default align values.yaml in chart
|
|
if i.chartArgs.imageRepo != "" {
|
|
valuesConfig = append(valuesConfig, fmt.Sprintf("image.repository=%s", i.chartArgs.imageRepo))
|
|
}
|
|
if i.chartArgs.imageTag != "" {
|
|
valuesConfig = append(valuesConfig, fmt.Sprintf("image.tag=%s", i.chartArgs.imageTag))
|
|
}
|
|
if i.chartArgs.imagePullPolicy != "" {
|
|
valuesConfig = append(valuesConfig, fmt.Sprintf("image.pullPolicy=%s", i.chartArgs.imagePullPolicy))
|
|
}
|
|
// TODO(wonderflow) values here could give more arguments in command line
|
|
|
|
for _, val := range valuesConfig {
|
|
// parses Helm strvals line and merges into a map for the final overrides for values.yaml
|
|
if err := strvals.ParseInto(val, finalValues); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return finalValues, nil
|
|
}
|
|
|
|
// InstallOamRuntime installs vela-core runtime from helm chart
|
|
func InstallOamRuntime(chartPath, chartSource string, vals map[string]interface{}, ioStreams cmdutil.IOStreams) error {
|
|
var err error
|
|
var chartRequested *chart.Chart
|
|
if chartPath != "" {
|
|
ioStreams.Infof("Use customized chart at: %s", chartPath)
|
|
chartRequested, err = loader.Load(chartPath)
|
|
} else {
|
|
chartRequested, err = cli.LoadChart(chartSource)
|
|
if chartRequested != nil {
|
|
m, l := chartRequested.Metadata, len(chartRequested.Raw)
|
|
ioStreams.Infof("install chart %s, version %s, desc : %s, contains %d file\n", m.Name, m.Version, m.Description, l)
|
|
}
|
|
}
|
|
if err != nil {
|
|
return fmt.Errorf("error loading chart for installation: %w", err)
|
|
}
|
|
installClient, err := helm.NewHelmInstall("", types.DefaultKubeVelaNS, types.DefaultKubeVelaReleaseName)
|
|
if err != nil {
|
|
return fmt.Errorf("error create helm install client: %w", err)
|
|
}
|
|
release, err := installClient.Run(chartRequested, vals)
|
|
if err != nil {
|
|
ioStreams.Errorf("Failed to install the chart with error: %+v\n", err)
|
|
return err
|
|
}
|
|
ioStreams.Infof("Successfully installed the chart, status: %s, last deployed time = %s\n",
|
|
release.Info.Status,
|
|
release.Info.LastDeployed.String())
|
|
return nil
|
|
}
|
|
|
|
// GetOAMReleaseVersion gets version of vela-core runtime helm release
|
|
func GetOAMReleaseVersion(ns string) (string, error) {
|
|
results, err := helm.GetHelmRelease(ns)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
for _, result := range results {
|
|
if result.Chart.ChartFullPath() == types.DefaultKubeVelaChartName {
|
|
return result.Chart.AppVersion(), nil
|
|
}
|
|
}
|
|
return "", errors.New("oam-kubernetes-runtime not found in your kubernetes cluster, try `vela install` to install")
|
|
}
|
|
|
|
// PrintTrackVelaRuntimeStatus prints status of installing vela-core runtime
|
|
func PrintTrackVelaRuntimeStatus(ctx context.Context, c client.Client, ioStreams cmdutil.IOStreams, trackTimeout time.Duration) (bool, error) {
|
|
trackInterval := 5 * time.Second
|
|
|
|
ioStreams.Info("\nIt may take 1-2 minutes before KubeVela runtime is ready.")
|
|
start := time.Now()
|
|
spiner := newTrackingSpinner("Waiting KubeVela runtime ready to serve ...")
|
|
spiner.Start()
|
|
defer spiner.Stop()
|
|
|
|
for {
|
|
timeConsumed := int(time.Since(start).Seconds())
|
|
applySpinnerNewSuffix(spiner, fmt.Sprintf("Waiting KubeVela runtime ready to serve (timeout %d/%d seconds) ...",
|
|
timeConsumed, int(trackTimeout.Seconds())))
|
|
|
|
sts, podName, err := getVelaRuntimeStatus(ctx, c)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if sts == Ready {
|
|
ioStreams.Info(fmt.Sprintf("\n%s %s", emojiSucceed, "KubeVela runtime is ready to serve!"))
|
|
return true, nil
|
|
}
|
|
// status except Ready results in re-check until timeout
|
|
if time.Since(start) > trackTimeout {
|
|
ioStreams.Info(fmt.Sprintf("\n%s %s", emojiFail, "KubeVela runtime starts timeout!"))
|
|
if len(podName) != 0 {
|
|
ioStreams.Info(fmt.Sprintf("\n%s %s%s", emojiLightBulb,
|
|
"Please use this command for more detail: ",
|
|
white.Sprintf("kubectl logs -f %s -n vela-system", podName)))
|
|
}
|
|
return false, nil
|
|
}
|
|
time.Sleep(trackInterval)
|
|
}
|
|
}
|
|
|
|
func getVelaRuntimeStatus(ctx context.Context, c client.Client) (VelaRuntimeStatus, string, error) {
|
|
podList := &corev1.PodList{}
|
|
opts := []client.ListOption{
|
|
client.InNamespace(types.DefaultKubeVelaNS),
|
|
client.MatchingLabels{
|
|
"app.kubernetes.io/name": types.DefaultKubeVelaChartName,
|
|
"app.kubernetes.io/instance": types.DefaultKubeVelaReleaseName,
|
|
},
|
|
}
|
|
if err := c.List(ctx, podList, opts...); err != nil {
|
|
return Error, "", err
|
|
}
|
|
if len(podList.Items) == 0 {
|
|
return NotFound, "", nil
|
|
}
|
|
runtimePod := podList.Items[0]
|
|
podName := runtimePod.GetName()
|
|
if runtimePod.Status.Phase == corev1.PodRunning {
|
|
// since readiness & liveness probes are set for vela container
|
|
// so check each condition is ready
|
|
for _, c := range runtimePod.Status.Conditions {
|
|
if c.Status != corev1.ConditionTrue {
|
|
return Pending, podName, nil
|
|
}
|
|
}
|
|
return Ready, podName, nil
|
|
}
|
|
return Pending, podName, nil
|
|
}
|