Feat: adapt vela port-forward with the velaql (#4439)

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
This commit is contained in:
Jianbo Sun
2022-07-25 21:27:31 +08:00
committed by GitHub
parent 26234f5ebf
commit 4814bce594
11 changed files with 199 additions and 209 deletions
+1
View File
@@ -40,4 +40,5 @@ WORKDIR /
ARG TARGETARCH
COPY --from=builder /workspace/vela-${TARGETARCH} /vela
COPY /vela /bin/
ENTRYPOINT ["/vela"]
+1 -10
View File
@@ -394,8 +394,7 @@ func clusterObjectReferenceTypeFilterGenerator(allowedKinds ...string) clusterOb
}
var isWorkloadClusterObjectReferenceFilter = clusterObjectReferenceTypeFilterGenerator("Deployment", "StatefulSet", "CloneSet", "Job", "Configuration")
var isPortForwardEndpointClusterObjectReferenceFilter = clusterObjectReferenceTypeFilterGenerator("Deployment",
"StatefulSet", "CloneSet", "Job", "Service", "HelmRelease")
var resourceNameClusterObjectReferenceFilter = func(resourceName []string) clusterObjectReferenceFilter {
return func(reference common.ClusterObjectReference) bool {
if len(resourceName) == 0 {
@@ -533,14 +532,6 @@ func AskToChooseOneEnvResource(app *v1beta1.Application, resourceName ...string)
return askToChooseOneResource(app, filters...)
}
// AskToChooseOnePortForwardEndpoint will ask user to select one applied resource as port forward endpoint
func AskToChooseOnePortForwardEndpoint(app *v1beta1.Application, resourceName ...string) (*common.ClusterObjectReference, error) {
filters := []clusterObjectReferenceFilter{isPortForwardEndpointClusterObjectReferenceFilter}
_resourceName := removeEmptyString(resourceName)
filters = append(filters, resourceNameClusterObjectReferenceFilter(_resourceName))
return askToChooseOneResource(app, filters...)
}
func askToChooseOneInApplication(category string, options []string) (decision string, err error) {
if len(options) == 0 {
return "", fmt.Errorf("no %s exists in the application", category)
+3
View File
@@ -47,6 +47,9 @@ type ServiceEndpoint struct {
// String return endpoint URL
func (s *ServiceEndpoint) String() string {
if s.Endpoint.Host == "" && s.Endpoint.Port == 0 {
return "-"
}
protocol := strings.ToLower(string(s.Endpoint.Protocol))
if s.Endpoint.AppProtocol != nil && *s.Endpoint.AppProtocol != "" {
protocol = *s.Endpoint.AppProtocol
+130 -152
View File
@@ -19,16 +19,15 @@ package cli
import (
"context"
"fmt"
"net"
"net/http"
"net/url"
"reflect"
"strconv"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/pkg/errors"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
types2 "k8s.io/apimachinery/pkg/types"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
@@ -39,21 +38,15 @@ import (
"k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/client"
common2 "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/multicluster"
"github.com/oam-dev/kubevela/pkg/utils/common"
"github.com/oam-dev/kubevela/pkg/utils/util"
types2 "github.com/oam-dev/kubevela/pkg/velaql/providers/query/types"
"github.com/oam-dev/kubevela/references/appfile"
)
const (
fluxcdNameLabel = "helm.toolkit.fluxcd.io/name"
fluxcdNameSpaceLabel = "helm.toolkit.fluxcd.io/namespace"
)
// VelaPortForwardOptions for vela port-forward
type VelaPortForwardOptions struct {
Cmd *cobra.Command
@@ -64,13 +57,12 @@ type VelaPortForwardOptions struct {
VelaC common.Args
Env *types.EnvMeta
App *v1beta1.Application
targetResource *common2.ClusterObjectReference
targetResource *types2.ServiceEndpoint
f k8scmdutil.Factory
kcPortForwardOptions *cmdpf.PortForwardOptions
ClientSet kubernetes.Interface
Client client.Client
routeTrait bool
namespace string
}
@@ -130,7 +122,6 @@ func NewPortForwardCommand(c common.Args, order string, ioStreams util.IOStreams
cmd.Flags().Duration(podRunningTimeoutFlag, defaultPodExecTimeout,
"The length of time (like 5s, 2m, or 3h, higher than zero) to wait until at least one pod is running",
)
cmd.Flags().BoolVar(&o.routeTrait, "route", false, "forward ports from route trait service")
addNamespaceAndEnvArg(cmd)
return cmd
@@ -148,30 +139,66 @@ func (o *VelaPortForwardOptions) Init(ctx context.Context, cmd *cobra.Command, a
}
o.App = app
targetResource, err := common.AskToChooseOnePortForwardEndpoint(o.App)
rawEndpoints, err := GetServiceEndpoints(o.Ctx, o.App.Name, o.namespace, o.VelaC, Filter{})
if err != nil {
return err
}
var endpoints []types2.ServiceEndpoint
for _, ep := range rawEndpoints {
if ep.Ref.Kind != "Service" {
continue
}
endpoints = append(endpoints, ep)
}
if len(endpoints) == 0 {
inSide := func(str string) bool {
for _, s := range []string{"Deployment", "StatefulSet", "CloneSet", "Job"} {
if str == s {
return true
}
}
return false
}
for _, ap := range app.Status.AppliedResources {
if !inSide(ap.Kind) {
continue
}
endpoints = append(endpoints, types2.ServiceEndpoint{
Endpoint: types2.Endpoint{},
Ref: corev1.ObjectReference{
Namespace: ap.Namespace,
Name: ap.Name,
Kind: ap.Kind,
APIVersion: ap.APIVersion,
},
Cluster: ap.Cluster,
})
}
}
targetResource, err := AskToChooseOnePortForwardEndpoint(endpoints)
if err != nil {
return err
}
cf := genericclioptions.NewConfigFlags(true)
cf.Namespace = pointer.String(targetResource.Namespace)
cf.Namespace = pointer.String(o.namespace)
cf.WrapConfigFn = func(cfg *rest.Config) *rest.Config {
cfg.Wrap(multicluster.NewClusterGatewayRoundTripperWrapperGenerator(targetResource.Cluster))
return cfg
}
o.f = k8scmdutil.NewFactory(k8scmdutil.NewMatchVersionFlags(cf))
o.targetResource = targetResource
o.targetResource = &targetResource
o.Ctx = multicluster.ContextWithClusterName(ctx, targetResource.Cluster)
config, err := o.VelaC.GetConfig()
if err != nil {
return err
}
config.Wrap(multicluster.NewSecretModeMultiClusterRoundTripper)
client, err := client.New(config, client.Options{Scheme: common.Scheme})
forwardClient, err := client.New(config, client.Options{Scheme: common.Scheme})
if err != nil {
return err
}
o.VelaC.SetClient(client)
o.VelaC.SetClient(forwardClient)
if o.ClientSet == nil {
c, err := kubernetes.NewForConfig(config)
if err != nil {
@@ -182,160 +209,79 @@ func (o *VelaPortForwardOptions) Init(ctx context.Context, cmd *cobra.Command, a
return nil
}
func getRouteServiceName(appconfig *v1alpha2.ApplicationConfiguration, svcName string) string {
for _, comp := range appconfig.Status.Workloads {
if comp.ComponentName != svcName {
continue
// getPortsFromApp works for compatible
func getPortsFromApp(app *v1beta1.Application) int {
if app == nil || len(app.Spec.Components) == 0 {
return 0
}
_, configs := appfile.GetApplicationSettings(app, app.Spec.Components[0].Name)
for k, v := range configs {
portConv := func(v interface{}) int {
switch pv := v.(type) {
case int:
return pv
case string:
data, err := strconv.ParseInt(pv, 10, 64)
if err != nil {
return 0
}
return int(data)
case float64:
return int(pv)
}
return 0
}
for _, tr := range comp.Traits {
// TODO check from Capability
if tr.Reference.Kind == "Route" && tr.Reference.APIVersion == "standard.oam.dev/v1alpha1" {
return tr.Reference.Name
if k == "port" {
return portConv(v)
}
if k == "ports" {
portArray := v.([]interface{})
for _, p := range portArray {
return portConv(p.(map[string]interface{})["port"])
}
}
}
return ""
}
func getSvcNameAndPortFromHelmRelease(ctx context.Context, cli client.Client, o common2.ClusterObjectReference) (string, string, error) {
svcList := corev1.ServiceList{}
if err := cli.List(ctx, &svcList, client.InNamespace(o.Namespace), client.MatchingLabels{
fluxcdNameLabel: o.Name,
fluxcdNameSpaceLabel: o.Namespace,
}); err != nil {
return "", "", err
}
for _, svc := range svcList.Items {
if strings.HasPrefix(svc.Name, o.Name) {
// avoid panic
if len(svc.Spec.Ports) == 0 {
continue
}
port := svc.Spec.Ports[0].Port
return svc.Name, strconv.Itoa(int(port)), nil
}
}
return "", "", fmt.Errorf("have not found svc from helmRelease: %s", o.Name)
return 0
}
// Complete will complete the config of port-forward
func (o *VelaPortForwardOptions) Complete() error {
client, err := o.VelaC.GetClient()
if err != nil {
return err
}
compName, err := getCompNameFromClusterObjectReference(o.Ctx, client, o.targetResource)
if err != nil {
return err
}
if compName == "" {
return fmt.Errorf("failed to get component name")
}
if o.routeTrait {
appconfig, err := appfile.GetAppConfig(o.Ctx, client, o.App, o.Env)
var forwardTypeName string
switch o.targetResource.Ref.Kind {
case "Service":
forwardTypeName = "svc/" + o.targetResource.Ref.Name
case "Deployment", "StatefulSet", "CloneSet", "Job":
var err error
forwardTypeName, err = getPodNameForResource(o.Ctx, o.ClientSet, o.targetResource.Ref.Name, o.targetResource.Ref.Namespace)
if err != nil {
return err
}
routeSvc := getRouteServiceName(appconfig, compName)
if routeSvc == "" {
return fmt.Errorf("no route trait found in %s %s", o.App.Name, compName)
}
var svc = corev1.Service{}
err = client.Get(o.Ctx, types2.NamespacedName{Name: routeSvc, Namespace: o.Env.Namespace}, &svc)
if err != nil {
return err
}
if len(svc.Spec.Ports) == 0 {
return fmt.Errorf("no port found in service %s", routeSvc)
}
val := strconv.Itoa(int(svc.Spec.Ports[0].Port))
if val == "80" {
val = "8080:80"
} else if val == "443" {
val = "8443:443"
}
o.Args = append(o.Args, val)
args := make([]string, len(o.Args))
copy(args, o.Args)
args[0] = "svc/" + routeSvc
return o.kcPortForwardOptions.Complete(o.f, o.Cmd, args)
}
if o.targetResource.Kind == "HelmRelease" {
svcName, port, err := getSvcNameAndPortFromHelmRelease(o.Ctx, o.Client, *o.targetResource)
if err != nil {
return err
}
var val string
switch port {
case "80":
val = "8080:80"
case "443":
val = "8443:443"
default:
val = net.JoinHostPort(port, port)
}
o.Args[0] = fmt.Sprintf("svc/%s", svcName)
o.Args = append(o.Args, val)
return o.kcPortForwardOptions.Complete(o.f, o.Cmd, o.Args)
}
var podName string
if o.targetResource.Kind == "Service" {
podName = "svc/" + o.targetResource.Name
} else {
podName, err = getPodNameForResource(o.Ctx, o.ClientSet, o.targetResource.Name, o.targetResource.Namespace)
if err != nil {
return err
}
}
if len(o.Args) < 2 {
var found bool
_, configs := appfile.GetApplicationSettings(o.App, compName)
for k, v := range configs {
portConv := func(o *VelaPortForwardOptions, v interface{}, k string) (bool, error) {
var val string
switch pv := v.(type) {
case int:
val = strconv.Itoa(pv)
case string:
val = pv
case float64:
val = strconv.Itoa(int(pv))
default:
return false, fmt.Errorf("invalid type '%s' of port %v", reflect.TypeOf(v), k)
}
if val == "80" {
val = "8080:80"
} else if val == "443" {
val = "8443:443"
}
o.Args = append(o.Args, val)
return true, nil
}
if k == "port" {
found, err = portConv(o, v, k)
if err != nil {
return err
}
}
if k == "ports" {
portArray := v.([]interface{})
for _, p := range portArray {
found, err = portConv(o, p.(map[string]interface{})["port"], k)
if err != nil {
return err
}
}
formatPort := func(p int) string {
val := strconv.Itoa(p)
if val == "80" {
val = "8080:80"
} else if val == "443" {
val = "8443:443"
}
return val
}
if !found {
return fmt.Errorf("no port found in app or arguments")
pt := o.targetResource.Endpoint.Port
if pt == 0 {
pt = getPortsFromApp(o.App)
}
if pt == 0 {
return errors.New("not port specified for port-forward")
}
o.Args = append(o.Args, formatPort(pt))
}
args := make([]string, len(o.Args))
copy(args, o.Args)
args[0] = podName
args[0] = forwardTypeName
o.ioStreams.Infof("trying to connect the remote endpoint %s ..", strings.Join(args, " "))
return o.kcPortForwardOptions.Complete(o.f, o.Cmd, args)
}
@@ -378,3 +324,35 @@ func (f *defaultPortForwarder) ForwardPorts(method string, url *url.URL, opts cm
}
return fw.ForwardPorts()
}
// AskToChooseOnePortForwardEndpoint will ask user to select one applied resource as port forward endpoint
func AskToChooseOnePortForwardEndpoint(endpoints []types2.ServiceEndpoint) (types2.ServiceEndpoint, error) {
if len(endpoints) == 0 {
return types2.ServiceEndpoint{}, errors.New("no endpoint found in your application")
}
if len(endpoints) == 1 {
return endpoints[0], nil
}
lines := formatEndpoints(endpoints)
header := strings.Join(lines[0], " | ")
var ops []string
for i := 1; i < len(lines); i++ {
ops = append(ops, strings.Join(lines[i], " | "))
}
prompt := &survey.Select{
Message: fmt.Sprintf("You have %d endpoints in your app. Please choose one:\n%s", len(ops), header),
Options: ops,
}
var selectedRsc string
err := survey.AskOne(prompt, &selectedRsc)
if err != nil {
return types2.ServiceEndpoint{}, fmt.Errorf("choosing endpoint err %w", err)
}
for k, resource := range ops {
if selectedRsc == resource {
return endpoints[k], nil
}
}
// it should never happen.
return types2.ServiceEndpoint{}, errors.New("no endpoint match for your choice")
}
+24 -7
View File
@@ -45,6 +45,7 @@ import (
"github.com/oam-dev/kubevela/pkg/resourcetracker"
"github.com/oam-dev/kubevela/pkg/utils/common"
cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
types2 "github.com/oam-dev/kubevela/pkg/velaql/providers/query/types"
"github.com/oam-dev/kubevela/references/appfile"
)
@@ -179,6 +180,22 @@ func printAppStatus(_ context.Context, c client.Client, ioStreams cmdutil.IOStre
return loopCheckStatus(c, ioStreams, appName, namespace)
}
func formatEndpoints(endpoints []types2.ServiceEndpoint) [][]string {
var result [][]string
result = append(result, []string{"Cluster", "Component", "Ref(Kind/Namespace/Name)", "Endpoint", "Inner"})
for _, endpoint := range endpoints {
if endpoint.Cluster == "" {
endpoint.Cluster = multicluster.ClusterLocalName
}
if endpoint.Component == "" {
endpoint.Component = "-"
}
result = append(result, []string{endpoint.Cluster, endpoint.Component, fmt.Sprintf("%s/%s/%s", endpoint.Ref.Kind, endpoint.Ref.Namespace, endpoint.Ref.Name), endpoint.String(), fmt.Sprintf("%v", endpoint.Endpoint.Inner)})
}
return result
}
func printAppEndpoints(ctx context.Context, appName string, namespace string, f Filter, velaC common.Args, skipEmptyTable bool) error {
config, err := velaC.GetConfig()
if err != nil {
@@ -188,7 +205,8 @@ func printAppEndpoints(ctx context.Context, appName string, namespace string, f
if err != nil {
return err
}
endpoints, err := GetServiceEndpoints(ctx, client, appName, namespace, velaC, f)
velaC.SetClient(client)
endpoints, err := GetServiceEndpoints(ctx, appName, namespace, velaC, f)
if err != nil {
return err
}
@@ -198,12 +216,11 @@ func printAppEndpoints(ctx context.Context, appName string, namespace string, f
fmt.Printf("Please access %s from the following endpoints:\n", appName)
table := tablewriter.NewWriter(os.Stdout)
table.SetColWidth(100)
table.SetHeader([]string{"Cluster", "Component", "Ref(Kind/Namespace/Name)", "Endpoint", "Inner"})
for _, endpoint := range endpoints {
if endpoint.Cluster == "" {
endpoint.Cluster = multicluster.ClusterLocalName
}
table.Append([]string{endpoint.Cluster, endpoint.Component, fmt.Sprintf("%s/%s/%s", endpoint.Ref.Kind, endpoint.Ref.Namespace, endpoint.Ref.Name), endpoint.String(), fmt.Sprintf("%v", endpoint.Endpoint.Inner)})
printablePoints := formatEndpoints(endpoints)
table.SetHeader(printablePoints[0])
for i := 1; i < len(printablePoints); i++ {
table.Append(printablePoints[i])
}
table.Render()
return nil
-23
View File
@@ -29,17 +29,12 @@ import (
"github.com/pkg/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/util/jsonpath"
"k8s.io/kubectl/pkg/cmd/get"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
common2 "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/utils/common"
)
@@ -60,24 +55,6 @@ func getPodNameForResource(ctx context.Context, clientSet kubernetes.Interface,
return common.AskToChooseOnePods(pods)
}
func getCompNameFromClusterObjectReference(ctx context.Context, k8sClient client.Client, r *common2.ClusterObjectReference) (string, error) {
u := &unstructured.Unstructured{}
u.SetGroupVersionKind(r.GroupVersionKind())
if err := k8sClient.Get(ctx, types.NamespacedName{Namespace: r.Namespace, Name: r.Name}, u); err != nil {
return "", err
}
labels := u.GetLabels()
if labels == nil {
return "", nil
}
// Addon observability --> some Helm typed components --> some fluxcd objects --> some services. Those services
// are not labeled with oam.LabelAppComponent
if r.Name == common.AddonObservabilityGrafanaSvc {
return r.Name, nil
}
return labels[oam.LabelAppComponent], nil
}
// UserInput user input in command
type UserInput struct {
Writer io.Writer
+13 -14
View File
@@ -25,7 +25,6 @@ import (
"strings"
"github.com/spf13/cobra"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/cue/model/value"
@@ -91,19 +90,15 @@ export: "status"
if cueFile == "" && querySts == "" && len(args) == 0 {
return fmt.Errorf("please specify at least one VelaQL statement or VelaQL file path")
}
newClient, err := c.GetClient()
if err != nil {
return err
}
if cueFile != "" {
return queryFromView(ctx, newClient, c, cueFile, cmd)
return queryFromView(ctx, c, cueFile, cmd)
}
if querySts == "" {
// for compatibility
querySts = args[0]
}
return queryFromStatement(ctx, newClient, c, querySts, cmd)
return queryFromStatement(ctx, c, querySts, cmd)
},
Annotations: map[string]string{
types.TagCommandOrder: order,
@@ -202,12 +197,12 @@ If view name cannot be inferred, or you are reading from stdin (-f -), you must
}
// queryFromStatement print velaQL result from query statement with inner query view
func queryFromStatement(ctx context.Context, client client.Client, velaC common.Args, velaQLStatement string, cmd *cobra.Command) error {
func queryFromStatement(ctx context.Context, velaC common.Args, velaQLStatement string, cmd *cobra.Command) error {
queryView, err := velaql.ParseVelaQL(velaQLStatement)
if err != nil {
return err
}
queryValue, err := QueryValue(ctx, client, velaC, &queryView)
queryValue, err := QueryValue(ctx, velaC, &queryView)
if err != nil {
return err
}
@@ -215,12 +210,12 @@ func queryFromStatement(ctx context.Context, client client.Client, velaC common.
}
// queryFromView print velaQL result from query view
func queryFromView(ctx context.Context, client client.Client, velaC common.Args, velaQLViewPath string, cmd *cobra.Command) error {
func queryFromView(ctx context.Context, velaC common.Args, velaQLViewPath string, cmd *cobra.Command) error {
queryView, err := velaql.ParseVelaQLFromPath(velaQLViewPath)
if err != nil {
return err
}
queryValue, err := QueryValue(ctx, client, velaC, queryView)
queryValue, err := QueryValue(ctx, velaC, queryView)
if err != nil {
return err
}
@@ -255,7 +250,7 @@ func MakeVelaQL(view string, params map[string]string, action string) string {
}
// GetServiceEndpoints get service endpoints by velaQL
func GetServiceEndpoints(ctx context.Context, client client.Client, appName string, namespace string, velaC common.Args, f Filter) ([]querytypes.ServiceEndpoint, error) {
func GetServiceEndpoints(ctx context.Context, appName string, namespace string, velaC common.Args, f Filter) ([]querytypes.ServiceEndpoint, error) {
params := map[string]string{
"appName": appName,
"appNs": namespace,
@@ -273,7 +268,7 @@ func GetServiceEndpoints(ctx context.Context, client client.Client, appName stri
if err != nil {
return nil, err
}
queryValue, err := QueryValue(ctx, client, velaC, &queryView)
queryValue, err := QueryValue(ctx, velaC, &queryView)
if err != nil {
return nil, err
}
@@ -291,7 +286,7 @@ func GetServiceEndpoints(ctx context.Context, client client.Client, appName stri
}
// QueryValue get queryValue from velaQL
func QueryValue(ctx context.Context, client client.Client, velaC common.Args, queryView *velaql.QueryView) (*value.Value, error) {
func QueryValue(ctx context.Context, velaC common.Args, queryView *velaql.QueryView) (*value.Value, error) {
dm, err := velaC.GetDiscoveryMapper()
if err != nil {
return nil, err
@@ -304,6 +299,10 @@ func QueryValue(ctx context.Context, client client.Client, velaC common.Args, qu
if err != nil {
return nil, err
}
client, err := velaC.GetClient()
if err != nil {
return nil, err
}
queryValue, err := velaql.NewViewHandler(client, config, dm, pd).QueryView(ctx, *queryView)
if err != nil {
return nil, err
+2 -2
View File
@@ -77,7 +77,7 @@ export: "status"
cmd := NewCommand()
var buff = bytes.NewBufferString("")
cmd.SetOut(buff)
Expect(queryFromView(context.TODO(), k8sClient, arg, name, cmd)).Should(BeNil())
Expect(queryFromView(context.TODO(), arg, name, cmd)).Should(BeNil())
Expect(strings.TrimSpace(buff.String())).Should(BeEquivalentTo("my-value"))
})
})
@@ -428,7 +428,7 @@ var _ = Describe("Test velaQL", func() {
Expect(err).Should(BeNil())
err = k8sClient.Create(context.Background(), &cm)
Expect(err).Should(BeNil())
endpoints, err := GetServiceEndpoints(context.TODO(), k8sClient, appName, namespace, arg, Filter{})
endpoints, err := GetServiceEndpoints(context.TODO(), appName, namespace, arg, Filter{})
Expect(err).Should(BeNil())
urls := []string{
"http://ingress.domain",
+3
View File
@@ -421,6 +421,9 @@ func (o *AppfileOptions) apply(app *corev1beta1.Application, scopes []oam.Object
func Info(app *corev1beta1.Application) string {
yellow := color.New(color.FgYellow)
appName := app.Name
if app.Namespace != "" && app.Namespace != "default" {
appName += " -n " + app.Namespace
}
var appUpMessage = "✅ App has been deployed 🚀🚀🚀\n" +
" Port forward: " + yellow.Sprintf("vela port-forward %s\n", appName) +
" SSH: " + yellow.Sprintf("vela exec %s\n", appName) +
@@ -0,0 +1,21 @@
```yaml
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: vela-doc
namespace: vela-system
spec:
components:
- name: frontend
type: webservice
properties:
image: oamdev/vela-cli:v1.5.0-beta.1
cmd: ["/vela","show"]
ports:
- port: 18081
expose: true
traits:
- type: service-account
properties:
name: kubevela-vela-core
```
+1 -1
View File
@@ -302,7 +302,7 @@ func extractTypeFromError(paraValue cue.Value) string {
}
str = err.Error()
sll := strings.Split(str, "cannot use value (")
if len(sll) < 1 {
if len(sll) < 2 {
return str
}
str = sll[1]