2021-03-26 15:24:19 +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.
|
|
|
|
|
*/
|
|
|
|
|
|
2020-11-16 18:55:01 +08:00
|
|
|
package common
|
2020-09-24 16:30:12 +08:00
|
|
|
|
|
|
|
|
import (
|
2020-12-18 10:18:16 +08:00
|
|
|
"bytes"
|
2020-11-18 16:08:16 +08:00
|
|
|
"context"
|
2020-12-18 10:18:16 +08:00
|
|
|
"encoding/json"
|
|
|
|
|
"errors"
|
2020-09-24 16:30:12 +08:00
|
|
|
"fmt"
|
2021-01-28 18:36:52 +08:00
|
|
|
"io"
|
2020-11-18 16:08:16 +08:00
|
|
|
"net/http"
|
2020-09-24 16:30:12 +08:00
|
|
|
"os"
|
2021-01-28 18:36:52 +08:00
|
|
|
"os/exec"
|
2020-12-18 10:18:16 +08:00
|
|
|
"path/filepath"
|
2020-09-24 16:30:12 +08:00
|
|
|
|
2020-12-18 10:18:16 +08:00
|
|
|
"cuelang.org/go/cue"
|
2021-08-11 13:59:46 +08:00
|
|
|
"cuelang.org/go/cue/ast"
|
2021-06-15 13:36:23 +08:00
|
|
|
"cuelang.org/go/cue/format"
|
2020-12-18 10:18:16 +08:00
|
|
|
"cuelang.org/go/encoding/openapi"
|
2021-02-20 04:11:26 +08:00
|
|
|
"github.com/AlecAivazis/survey/v2"
|
2021-06-03 11:23:20 +08:00
|
|
|
"github.com/hashicorp/hcl/v2/hclparse"
|
2021-11-07 08:54:48 +08:00
|
|
|
clustergatewayapi "github.com/oam-dev/cluster-gateway/pkg/apis/cluster/v1alpha1"
|
2021-06-03 11:23:20 +08:00
|
|
|
"github.com/oam-dev/terraform-config-inspect/tfconfig"
|
2021-05-13 21:46:45 +08:00
|
|
|
terraformv1beta1 "github.com/oam-dev/terraform-controller/api/v1beta1"
|
2021-03-24 08:01:24 +08:00
|
|
|
kruise "github.com/openkruise/kruise-api/apps/v1alpha1"
|
2021-11-09 19:08:47 +08:00
|
|
|
errors2 "github.com/pkg/errors"
|
2020-10-15 11:51:41 +08:00
|
|
|
certmanager "github.com/wonderflow/cert-manager-api/pkg/apis/certmanager/v1"
|
2021-03-24 08:01:24 +08:00
|
|
|
istioclientv1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1"
|
2021-12-25 10:36:54 +08:00
|
|
|
v1 "k8s.io/api/core/v1"
|
2021-03-24 08:01:24 +08:00
|
|
|
crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
2021-07-30 10:02:51 +08:00
|
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
2020-11-17 14:25:58 +08:00
|
|
|
k8sruntime "k8s.io/apimachinery/pkg/runtime"
|
2021-07-30 10:02:51 +08:00
|
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
2020-09-24 16:30:12 +08:00
|
|
|
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
2021-09-14 20:35:10 +08:00
|
|
|
apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
|
2021-11-02 17:20:00 +08:00
|
|
|
ocmclusterv1 "open-cluster-management.io/api/cluster/v1"
|
2021-08-06 10:46:31 +08:00
|
|
|
ocmclusterv1alpha1 "open-cluster-management.io/api/cluster/v1alpha1"
|
|
|
|
|
ocmworkv1 "open-cluster-management.io/api/work/v1"
|
2021-08-11 23:05:10 +08:00
|
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
2020-11-17 14:25:58 +08:00
|
|
|
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
2021-08-15 10:53:05 +08:00
|
|
|
"sigs.k8s.io/yaml"
|
2020-09-24 16:30:12 +08:00
|
|
|
|
2021-03-24 08:01:24 +08:00
|
|
|
oamcore "github.com/oam-dev/kubevela/apis/core.oam.dev"
|
2021-11-02 14:45:15 +08:00
|
|
|
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
2021-11-07 08:54:48 +08:00
|
|
|
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
2021-03-24 08:01:24 +08:00
|
|
|
oamstandard "github.com/oam-dev/kubevela/apis/standard.oam.dev/v1alpha1"
|
2021-12-22 21:08:00 +08:00
|
|
|
"github.com/oam-dev/kubevela/apis/types"
|
2021-06-02 15:37:06 +08:00
|
|
|
velacue "github.com/oam-dev/kubevela/pkg/cue"
|
2021-08-30 11:43:20 +08:00
|
|
|
"github.com/oam-dev/kubevela/pkg/cue/model"
|
2021-12-25 10:36:54 +08:00
|
|
|
"github.com/oam-dev/kubevela/pkg/oam"
|
2020-09-24 16:30:12 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
2020-11-17 17:46:56 +08:00
|
|
|
// Scheme defines the default KubeVela schema
|
2020-09-24 16:30:12 +08:00
|
|
|
Scheme = k8sruntime.NewScheme()
|
|
|
|
|
)
|
|
|
|
|
|
2021-12-22 21:08:00 +08:00
|
|
|
const (
|
|
|
|
|
// AddonObservabilityApplication is the application name for Addon Observability
|
|
|
|
|
AddonObservabilityApplication = "addon-observability"
|
|
|
|
|
// AddonObservabilityGrafanaSvc is grafana service name for Addon Observability
|
|
|
|
|
AddonObservabilityGrafanaSvc = "grafana"
|
|
|
|
|
)
|
|
|
|
|
|
2021-12-25 10:36:54 +08:00
|
|
|
// CreateCustomNamespace display the create namespace message
|
|
|
|
|
const CreateCustomNamespace = "create new namespace"
|
|
|
|
|
|
2020-09-24 16:30:12 +08:00
|
|
|
func init() {
|
|
|
|
|
_ = clientgoscheme.AddToScheme(Scheme)
|
2021-09-14 20:35:10 +08:00
|
|
|
_ = apiregistrationv1.AddToScheme(Scheme)
|
2021-03-24 08:01:24 +08:00
|
|
|
_ = crdv1.AddToScheme(Scheme)
|
|
|
|
|
_ = oamcore.AddToScheme(Scheme)
|
|
|
|
|
_ = oamstandard.AddToScheme(Scheme)
|
|
|
|
|
_ = istioclientv1beta1.AddToScheme(Scheme)
|
2020-09-24 16:30:12 +08:00
|
|
|
_ = certmanager.AddToScheme(Scheme)
|
2021-03-24 08:01:24 +08:00
|
|
|
_ = kruise.AddToScheme(Scheme)
|
2021-05-13 21:46:45 +08:00
|
|
|
_ = terraformv1beta1.AddToScheme(Scheme)
|
2021-08-06 10:46:31 +08:00
|
|
|
_ = ocmclusterv1alpha1.Install(Scheme)
|
2021-11-02 17:20:00 +08:00
|
|
|
_ = ocmclusterv1.Install(Scheme)
|
2021-08-06 10:46:31 +08:00
|
|
|
_ = ocmworkv1.Install(Scheme)
|
2021-11-07 08:54:48 +08:00
|
|
|
_ = clustergatewayapi.AddToScheme(Scheme)
|
2020-09-24 16:30:12 +08:00
|
|
|
// +kubebuilder:scaffold:scheme
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-17 17:46:56 +08:00
|
|
|
// InitBaseRestConfig will return reset config for create controller runtime client
|
2021-03-23 16:30:49 +08:00
|
|
|
func InitBaseRestConfig() (Args, error) {
|
2022-01-07 15:49:27 +08:00
|
|
|
args := Args{
|
|
|
|
|
Schema: Scheme,
|
|
|
|
|
}
|
|
|
|
|
_, err := args.GetConfig()
|
2021-08-06 17:10:52 +08:00
|
|
|
if err != nil && os.Getenv("IGNORE_KUBE_CONFIG") != "true" {
|
2020-09-24 16:30:12 +08:00
|
|
|
fmt.Println("get kubeConfig err", err)
|
|
|
|
|
os.Exit(1)
|
2021-10-29 15:07:15 +08:00
|
|
|
} else if err != nil {
|
|
|
|
|
return Args{}, err
|
2020-09-24 16:30:12 +08:00
|
|
|
}
|
2022-01-07 15:49:27 +08:00
|
|
|
return args, nil
|
2020-09-24 16:30:12 +08:00
|
|
|
}
|
2020-11-18 16:08:16 +08:00
|
|
|
|
2021-10-08 13:10:47 +08:00
|
|
|
// globalClient will be a client for whole command lifecycle
|
|
|
|
|
var globalClient client.Client
|
|
|
|
|
|
|
|
|
|
// SetGlobalClient will set a client for one cli command
|
|
|
|
|
func SetGlobalClient(clt client.Client) error {
|
|
|
|
|
globalClient = clt
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetClient will K8s client in args
|
|
|
|
|
func GetClient() (client.Client, error) {
|
|
|
|
|
if globalClient != nil {
|
|
|
|
|
return globalClient, nil
|
|
|
|
|
}
|
|
|
|
|
return nil, errors.New("client not set, call SetGlobalClient first")
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-18 16:08:16 +08:00
|
|
|
// HTTPGet will send GET http request with context
|
|
|
|
|
func HTTPGet(ctx context.Context, url string) ([]byte, error) {
|
|
|
|
|
// Change NewRequest to NewRequestWithContext and pass context it
|
|
|
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
//nolint:errcheck
|
|
|
|
|
defer resp.Body.Close()
|
2021-09-06 18:33:42 +08:00
|
|
|
return io.ReadAll(resp.Body)
|
2020-11-18 16:08:16 +08:00
|
|
|
}
|
2020-12-18 10:18:16 +08:00
|
|
|
|
|
|
|
|
// GetCUEParameterValue converts definitions to cue format
|
|
|
|
|
func GetCUEParameterValue(cueStr string) (cue.Value, error) {
|
|
|
|
|
r := cue.Runtime{}
|
2021-06-02 15:37:06 +08:00
|
|
|
template, err := r.Compile("", cueStr+velacue.BaseTemplate)
|
2020-12-18 10:18:16 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return cue.Value{}, err
|
|
|
|
|
}
|
|
|
|
|
tempStruct, err := template.Value().Struct()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return cue.Value{}, err
|
|
|
|
|
}
|
|
|
|
|
// find the parameter definition
|
|
|
|
|
var paraDef cue.FieldInfo
|
|
|
|
|
var found bool
|
|
|
|
|
for i := 0; i < tempStruct.Len(); i++ {
|
|
|
|
|
paraDef = tempStruct.Field(i)
|
2021-08-30 11:43:20 +08:00
|
|
|
if paraDef.Name == model.ParameterFieldName {
|
2020-12-18 10:18:16 +08:00
|
|
|
found = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !found {
|
|
|
|
|
return cue.Value{}, errors.New("parameter not exist")
|
|
|
|
|
}
|
|
|
|
|
arguments := paraDef.Value
|
|
|
|
|
|
|
|
|
|
return arguments, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GenOpenAPI generates OpenAPI json schema from cue.Instance
|
|
|
|
|
func GenOpenAPI(inst *cue.Instance) ([]byte, error) {
|
|
|
|
|
if inst.Err != nil {
|
|
|
|
|
return nil, inst.Err
|
|
|
|
|
}
|
2021-06-15 13:36:23 +08:00
|
|
|
paramOnlyIns, err := RefineParameterInstance(inst)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2020-12-18 10:18:16 +08:00
|
|
|
defaultConfig := &openapi.Config{}
|
2021-06-15 13:36:23 +08:00
|
|
|
b, err := openapi.Gen(paramOnlyIns, defaultConfig)
|
2020-12-18 10:18:16 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
var out = &bytes.Buffer{}
|
|
|
|
|
_ = json.Indent(out, b, "", " ")
|
|
|
|
|
return out.Bytes(), nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-11 13:59:46 +08:00
|
|
|
// extractParameterDefinitionNodeFromInstance extracts the `#parameter` ast.Node from root instance, if failed fall back to `parameter` by LookUpDef
|
|
|
|
|
func extractParameterDefinitionNodeFromInstance(inst *cue.Instance) ast.Node {
|
|
|
|
|
opts := []cue.Option{cue.All(), cue.DisallowCycles(true), cue.ResolveReferences(true), cue.Docs(true)}
|
|
|
|
|
node := inst.Value().Syntax(opts...)
|
|
|
|
|
if fileNode, ok := node.(*ast.File); ok {
|
|
|
|
|
for _, decl := range fileNode.Decls {
|
|
|
|
|
if field, ok := decl.(*ast.Field); ok {
|
2021-08-30 11:43:20 +08:00
|
|
|
if label, ok := field.Label.(*ast.Ident); ok && label.Name == "#"+model.ParameterFieldName {
|
2021-08-11 13:59:46 +08:00
|
|
|
return decl.(*ast.Field).Value
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-08-30 11:43:20 +08:00
|
|
|
paramVal := inst.LookupDef(model.ParameterFieldName)
|
2021-08-11 13:59:46 +08:00
|
|
|
return paramVal.Syntax(opts...)
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-15 13:36:23 +08:00
|
|
|
// RefineParameterInstance refines cue instance to merely include `parameter` identifier
|
|
|
|
|
func RefineParameterInstance(inst *cue.Instance) (*cue.Instance, error) {
|
|
|
|
|
r := cue.Runtime{}
|
2021-08-30 11:43:20 +08:00
|
|
|
paramVal := inst.LookupDef(model.ParameterFieldName)
|
2021-06-15 13:36:23 +08:00
|
|
|
var paramOnlyStr string
|
|
|
|
|
switch k := paramVal.IncompleteKind(); k {
|
|
|
|
|
case cue.StructKind, cue.ListKind:
|
2021-08-11 13:59:46 +08:00
|
|
|
paramSyntax, _ := format.Node(extractParameterDefinitionNodeFromInstance(inst))
|
2021-08-30 11:43:20 +08:00
|
|
|
paramOnlyStr = fmt.Sprintf("#%s: %s\n", model.ParameterFieldName, string(paramSyntax))
|
2021-06-15 13:36:23 +08:00
|
|
|
case cue.IntKind, cue.StringKind, cue.FloatKind, cue.BoolKind:
|
2021-08-30 11:43:20 +08:00
|
|
|
paramOnlyStr = fmt.Sprintf("#%s: %v", model.ParameterFieldName, paramVal)
|
2021-06-15 13:36:23 +08:00
|
|
|
case cue.BottomKind:
|
2021-08-30 11:43:20 +08:00
|
|
|
paramOnlyStr = fmt.Sprintf("#%s: {}", model.ParameterFieldName)
|
2021-06-15 13:36:23 +08:00
|
|
|
default:
|
|
|
|
|
return nil, fmt.Errorf("unsupport parameter kind: %s", k.String())
|
|
|
|
|
}
|
|
|
|
|
paramOnlyIns, err := r.Compile("-", paramOnlyStr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return paramOnlyIns, nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-28 18:36:52 +08:00
|
|
|
// RealtimePrintCommandOutput prints command output in real time
|
|
|
|
|
// If logFile is "", it will prints the stdout, or it will write to local file
|
|
|
|
|
func RealtimePrintCommandOutput(cmd *exec.Cmd, logFile string) error {
|
|
|
|
|
var writer io.Writer
|
|
|
|
|
if logFile == "" {
|
|
|
|
|
writer = io.MultiWriter(os.Stdout)
|
|
|
|
|
} else {
|
|
|
|
|
if _, err := os.Stat(filepath.Dir(logFile)); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
f, err := os.Create(filepath.Clean(logFile))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
writer = io.MultiWriter(f)
|
|
|
|
|
}
|
|
|
|
|
cmd.Stdout = writer
|
|
|
|
|
cmd.Stderr = writer
|
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2021-02-20 04:11:26 +08:00
|
|
|
|
2021-11-02 14:45:15 +08:00
|
|
|
// ClusterObject2Map convert ClusterObjectReference to a readable map
|
|
|
|
|
func ClusterObject2Map(refs []common.ClusterObjectReference) map[string]string {
|
2021-11-07 17:25:52 +08:00
|
|
|
clusterResourceRefTmpl := "Cluster: %s | Namespace: %s | Component: %s | Kind: %s"
|
2021-11-02 14:45:15 +08:00
|
|
|
objs := make(map[string]string, len(refs))
|
|
|
|
|
for _, r := range refs {
|
|
|
|
|
if r.Cluster == "" {
|
|
|
|
|
r.Cluster = "local"
|
|
|
|
|
}
|
2021-11-09 19:08:47 +08:00
|
|
|
objs[r.Cluster+"/"+r.Namespace+"/"+r.Name+"/"+r.Kind] = fmt.Sprintf(clusterResourceRefTmpl, r.Cluster, r.Namespace, r.Name, r.Kind)
|
2021-11-02 14:45:15 +08:00
|
|
|
}
|
|
|
|
|
return objs
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-07 08:54:48 +08:00
|
|
|
// ResourceLocation indicates the resource location
|
|
|
|
|
type ResourceLocation struct {
|
|
|
|
|
Cluster string
|
|
|
|
|
Namespace string
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-09 19:08:47 +08:00
|
|
|
type clusterObjectReferenceFilter func(common.ClusterObjectReference) bool
|
|
|
|
|
|
|
|
|
|
func clusterObjectReferenceTypeFilterGenerator(allowedKinds ...string) clusterObjectReferenceFilter {
|
|
|
|
|
allowedKindMap := map[string]bool{}
|
|
|
|
|
for _, allowedKind := range allowedKinds {
|
|
|
|
|
allowedKindMap[allowedKind] = true
|
|
|
|
|
}
|
|
|
|
|
return func(item common.ClusterObjectReference) bool {
|
|
|
|
|
_, exists := allowedKindMap[item.Kind]
|
|
|
|
|
return exists
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var isWorkloadClusterObjectReferenceFilter = clusterObjectReferenceTypeFilterGenerator("Deployment", "StatefulSet", "CloneSet", "Job", "Configuration")
|
2021-12-22 21:08:00 +08:00
|
|
|
var isPortForwardEndpointClusterObjectReferenceFilter = clusterObjectReferenceTypeFilterGenerator("Deployment",
|
|
|
|
|
"StatefulSet", "CloneSet", "Job", "Service", "HelmRelease")
|
2021-11-09 19:08:47 +08:00
|
|
|
|
|
|
|
|
func filterResource(inputs []common.ClusterObjectReference, filters ...clusterObjectReferenceFilter) (outputs []common.ClusterObjectReference) {
|
|
|
|
|
for _, item := range inputs {
|
|
|
|
|
flag := true
|
|
|
|
|
for _, filter := range filters {
|
|
|
|
|
if !filter(item) {
|
|
|
|
|
flag = false
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if flag {
|
|
|
|
|
outputs = append(outputs, item)
|
2021-11-07 08:54:48 +08:00
|
|
|
}
|
|
|
|
|
}
|
2021-11-09 19:08:47 +08:00
|
|
|
return
|
2021-11-07 08:54:48 +08:00
|
|
|
}
|
|
|
|
|
|
2021-11-09 19:08:47 +08:00
|
|
|
func askToChooseOneResource(app *v1beta1.Application, filters ...clusterObjectReferenceFilter) (*common.ClusterObjectReference, error) {
|
2021-11-07 08:54:48 +08:00
|
|
|
resources := app.Status.AppliedResources
|
|
|
|
|
if len(resources) == 0 {
|
|
|
|
|
return nil, fmt.Errorf("no resources in the application deployed yet")
|
|
|
|
|
}
|
2021-11-09 19:08:47 +08:00
|
|
|
resources = filterResource(resources, filters...)
|
2021-12-22 21:08:00 +08:00
|
|
|
if app.Name == AddonObservabilityApplication {
|
|
|
|
|
resources = filterClusterObjectRefFromAddonObservability(resources)
|
|
|
|
|
}
|
2021-11-07 08:54:48 +08:00
|
|
|
// filter locations
|
2021-11-02 14:45:15 +08:00
|
|
|
if len(resources) == 0 {
|
2021-11-09 19:08:47 +08:00
|
|
|
return nil, fmt.Errorf("no supported resources detected in deployed resources")
|
2021-11-02 14:45:15 +08:00
|
|
|
}
|
|
|
|
|
if len(resources) == 1 {
|
|
|
|
|
return &resources[0], nil
|
|
|
|
|
}
|
|
|
|
|
opMap := ClusterObject2Map(resources)
|
|
|
|
|
var ops []string
|
|
|
|
|
for _, r := range opMap {
|
|
|
|
|
ops = append(ops, r)
|
|
|
|
|
}
|
|
|
|
|
prompt := &survey.Select{
|
2021-11-07 08:54:48 +08:00
|
|
|
Message: fmt.Sprintf("You have %d deployed resources in your app. Please choose one:", len(ops)),
|
2021-11-02 14:45:15 +08:00
|
|
|
Options: ops,
|
|
|
|
|
}
|
|
|
|
|
var selectedRsc string
|
|
|
|
|
err := survey.AskOne(prompt, &selectedRsc)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("choosing resource err %w", err)
|
|
|
|
|
}
|
|
|
|
|
for k, resource := range ops {
|
|
|
|
|
if selectedRsc == resource {
|
|
|
|
|
return &resources[k], nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil, fmt.Errorf("choosing resource err %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-25 10:36:54 +08:00
|
|
|
// AskToChooseOneNamespace ask for choose one namespace as env
|
|
|
|
|
func AskToChooseOneNamespace(c client.Client, envMeta *types.EnvMeta) error {
|
|
|
|
|
var nsList v1.NamespaceList
|
|
|
|
|
if err := c.List(context.TODO(), &nsList); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
var ops = []string{CreateCustomNamespace}
|
|
|
|
|
for _, r := range nsList.Items {
|
|
|
|
|
ops = append(ops, r.Name)
|
|
|
|
|
}
|
|
|
|
|
prompt := &survey.Select{
|
|
|
|
|
Message: "Would you like to choose an existing namespaces as your env?",
|
|
|
|
|
Options: ops,
|
|
|
|
|
}
|
|
|
|
|
err := survey.AskOne(prompt, &envMeta.Namespace)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("choosing namespace err %w", err)
|
|
|
|
|
}
|
|
|
|
|
if envMeta.Namespace == CreateCustomNamespace {
|
|
|
|
|
err = survey.AskOne(&survey.Input{
|
|
|
|
|
Message: "Please name the new namespace:",
|
|
|
|
|
}, &envMeta.Namespace)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
for _, ns := range nsList.Items {
|
|
|
|
|
if ns.Name == envMeta.Namespace && envMeta.Name == "" {
|
|
|
|
|
envMeta.Name = ns.Labels[oam.LabelNamespaceOfEnvName]
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-22 21:08:00 +08:00
|
|
|
func filterClusterObjectRefFromAddonObservability(resources []common.ClusterObjectReference) []common.ClusterObjectReference {
|
|
|
|
|
var observabilityResources []common.ClusterObjectReference
|
|
|
|
|
for _, res := range resources {
|
|
|
|
|
if res.Namespace == types.DefaultKubeVelaNS && res.Name == AddonObservabilityGrafanaSvc {
|
|
|
|
|
res.Kind = "Service"
|
|
|
|
|
res.APIVersion = "v1"
|
|
|
|
|
observabilityResources = append(observabilityResources, res)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
resources = observabilityResources
|
|
|
|
|
return resources
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-09 19:08:47 +08:00
|
|
|
// AskToChooseOneEnvResource will ask users to select one applied resource of the application if more than one
|
|
|
|
|
// resource is a map for component to applied resources
|
|
|
|
|
// return the selected ClusterObjectReference
|
|
|
|
|
func AskToChooseOneEnvResource(app *v1beta1.Application) (*common.ClusterObjectReference, error) {
|
|
|
|
|
return askToChooseOneResource(app, isWorkloadClusterObjectReferenceFilter)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AskToChooseOnePortForwardEndpoint will ask user to select one applied resource as port forward endpoint
|
|
|
|
|
func AskToChooseOnePortForwardEndpoint(app *v1beta1.Application) (*common.ClusterObjectReference, error) {
|
|
|
|
|
return askToChooseOneResource(app, isPortForwardEndpointClusterObjectReferenceFilter)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func askToChooseOneInApplication(category string, options []string) (decision string, err error) {
|
|
|
|
|
if len(options) == 0 {
|
|
|
|
|
return "", fmt.Errorf("no %s exists in the application", category)
|
2021-02-20 04:11:26 +08:00
|
|
|
}
|
2021-11-09 19:08:47 +08:00
|
|
|
if len(options) == 1 {
|
|
|
|
|
return options[0], nil
|
2021-02-20 04:11:26 +08:00
|
|
|
}
|
|
|
|
|
prompt := &survey.Select{
|
2021-11-09 19:08:47 +08:00
|
|
|
Message: fmt.Sprintf("You have multiple %ss in your app. Please choose one %s: ", category, category),
|
|
|
|
|
Options: options,
|
2021-02-20 04:11:26 +08:00
|
|
|
}
|
2021-11-09 19:08:47 +08:00
|
|
|
if err = survey.AskOne(prompt, &decision); err != nil {
|
|
|
|
|
return "", errors2.Wrapf(err, "choosing %s failed", category)
|
2021-02-20 04:11:26 +08:00
|
|
|
}
|
2021-11-09 19:08:47 +08:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AskToChooseOneService will ask users to select one service of the application if more than one
|
|
|
|
|
func AskToChooseOneService(svcNames []string) (string, error) {
|
|
|
|
|
return askToChooseOneInApplication("service", svcNames)
|
2021-02-20 04:11:26 +08:00
|
|
|
}
|
2021-03-20 12:08:15 +08:00
|
|
|
|
2021-11-07 08:54:48 +08:00
|
|
|
// AskToChooseOnePods will ask users to select one pods of the resource if more than one
|
|
|
|
|
func AskToChooseOnePods(podNames []string) (string, error) {
|
2021-11-09 19:08:47 +08:00
|
|
|
return askToChooseOneInApplication("pod", podNames)
|
2021-11-07 08:54:48 +08:00
|
|
|
}
|
|
|
|
|
|
2021-03-20 12:08:15 +08:00
|
|
|
// ReadYamlToObject will read a yaml K8s object to runtime.Object
|
|
|
|
|
func ReadYamlToObject(path string, object k8sruntime.Object) error {
|
2021-09-06 18:33:42 +08:00
|
|
|
data, err := os.ReadFile(filepath.Clean(path))
|
2021-03-20 12:08:15 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return yaml.Unmarshal(data, object)
|
|
|
|
|
}
|
2021-06-03 11:23:20 +08:00
|
|
|
|
|
|
|
|
// ParseTerraformVariables get variables from Terraform Configuration
|
2022-01-05 19:45:30 +08:00
|
|
|
func ParseTerraformVariables(configuration string) (map[string]*tfconfig.Variable, map[string]*tfconfig.Output, error) {
|
2021-06-03 11:23:20 +08:00
|
|
|
p := hclparse.NewParser()
|
|
|
|
|
hclFile, diagnostic := p.ParseHCL([]byte(configuration), "")
|
|
|
|
|
if diagnostic != nil {
|
2022-01-05 19:45:30 +08:00
|
|
|
return nil, nil, errors.New(diagnostic.Error())
|
2021-06-03 11:23:20 +08:00
|
|
|
}
|
2022-01-05 19:45:30 +08:00
|
|
|
mod := tfconfig.Module{Variables: map[string]*tfconfig.Variable{}, Outputs: map[string]*tfconfig.Output{}}
|
2021-06-03 11:23:20 +08:00
|
|
|
diagnostic = tfconfig.LoadModuleFromFile(hclFile, &mod)
|
|
|
|
|
if diagnostic != nil {
|
2022-01-05 19:45:30 +08:00
|
|
|
return nil, nil, errors.New(diagnostic.Error())
|
2021-06-03 11:23:20 +08:00
|
|
|
}
|
2022-01-05 19:45:30 +08:00
|
|
|
return mod.Variables, mod.Outputs, nil
|
2021-06-03 11:23:20 +08:00
|
|
|
}
|
2021-07-30 10:02:51 +08:00
|
|
|
|
|
|
|
|
// GenerateUnstructuredObj generate UnstructuredObj
|
|
|
|
|
func GenerateUnstructuredObj(name, ns string, gvk schema.GroupVersionKind) *unstructured.Unstructured {
|
|
|
|
|
u := &unstructured.Unstructured{}
|
|
|
|
|
u.SetGroupVersionKind(gvk)
|
|
|
|
|
u.SetName(name)
|
|
|
|
|
u.SetNamespace(ns)
|
|
|
|
|
return u
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetSpecObjIntoUnstructuredObj set UnstructuredObj spec field
|
|
|
|
|
func SetSpecObjIntoUnstructuredObj(spec interface{}, u *unstructured.Unstructured) error {
|
|
|
|
|
bts, err := json.Marshal(spec)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
data := make(map[string]interface{})
|
|
|
|
|
if err := json.Unmarshal(bts, &data); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
_ = unstructured.SetNestedMap(u.Object, data, "spec")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2021-08-11 23:05:10 +08:00
|
|
|
|
|
|
|
|
// NewK8sClient init a local k8s client which add oamcore scheme
|
|
|
|
|
func NewK8sClient() (client.Client, error) {
|
|
|
|
|
conf, err := config.GetConfig()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
scheme := k8sruntime.NewScheme()
|
|
|
|
|
if err := clientgoscheme.AddToScheme(scheme); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if err := oamcore.AddToScheme(scheme); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
k8sClient, err := client.New(conf, client.Options{Scheme: scheme})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return k8sClient, nil
|
|
|
|
|
}
|