This is an automated email from the ASF dual-hosted git repository. tsato pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-k.git
The following commit(s) were added to refs/heads/main by this push: new 1cc067e feat(cli): initial support for describing Kamelets with kamel describe #2312 1cc067e is described below commit 1cc067e9a8e41ecb9eecc99d3123a3547f9bf6ee Author: Tadayoshi Sato <sato.tadayo...@gmail.com> AuthorDate: Wed Jun 2 14:25:01 2021 +0900 feat(cli): initial support for describing Kamelets with kamel describe #2312 --- pkg/apis/camel/v1alpha1/jsonschema_types.go | 14 +- pkg/apis/camel/v1alpha1/kamelet_types_support.go | 65 +++++++++ pkg/cmd/describe.go | 1 + pkg/cmd/describe_kamelet.go | 172 +++++++++++++++++++++++ 4 files changed, 251 insertions(+), 1 deletion(-) diff --git a/pkg/apis/camel/v1alpha1/jsonschema_types.go b/pkg/apis/camel/v1alpha1/jsonschema_types.go index db3b10a..038307b 100644 --- a/pkg/apis/camel/v1alpha1/jsonschema_types.go +++ b/pkg/apis/camel/v1alpha1/jsonschema_types.go @@ -123,6 +123,18 @@ func (m *RawMessage) UnmarshalJSON(data []byte) error { return nil } +// String returns a string representation of RawMessage +func (m *RawMessage) String() string { + if m == nil { + return "" + } + b, err := m.MarshalJSON() + if err != nil { + return "" + } + return string(b) +} + var _ json.Marshaler = (*RawMessage)(nil) var _ json.Unmarshaler = (*RawMessage)(nil) @@ -133,4 +145,4 @@ type JSONSchemaURL string type ExternalDocumentation struct { Description string `json:"description,omitempty""` URL string `json:"url,omitempty"` -} \ No newline at end of file +} diff --git a/pkg/apis/camel/v1alpha1/kamelet_types_support.go b/pkg/apis/camel/v1alpha1/kamelet_types_support.go index 9e6f540..52635f2 100644 --- a/pkg/apis/camel/v1alpha1/kamelet_types_support.go +++ b/pkg/apis/camel/v1alpha1/kamelet_types_support.go @@ -18,6 +18,8 @@ limitations under the License. package v1alpha1 import ( + "sort" + v1 "github.com/apache/camel-k/pkg/apis/camel/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -137,6 +139,45 @@ func (in *KameletStatus) RemoveCondition(condType KameletConditionType) { in.Conditions = newConditions } +// SortedDefinitionPropertiesKeys returns the sorted keys of the Kamelet definition properties +func (k *Kamelet) SortedDefinitionPropertiesKeys() []string { + if k.Spec.Definition == nil { + return []string{} + } + props := k.Spec.Definition.Properties + if len(props) == 0 { + return []string{} + } + res := make([]string, len(props)) + i := 0 + for key := range props { + res[i] = string(key) + i++ + } + sort.Strings(res) + return res +} + +// SortedTypesKeys returns the sorted keys of the Kamelet spec types +func (k *Kamelet) SortedTypesKeys() []EventSlot { + types := k.Spec.Types + if len(types) == 0 { + return []EventSlot{} + } + strs := make([]string, len(types)) + i := 0 + for key := range types { + strs[i] = string(key) + i++ + } + sort.Strings(strs) + res := make([]EventSlot, len(types)) + for i, s := range strs { + res[i] = EventSlot(s) + } + return res +} + func ValidKameletName(name string) bool { return !reservedKameletNames[name] } @@ -150,3 +191,27 @@ func ValidKameletProperties(kamelet *Kamelet) bool { } return true } + +// NewKamelet creates a new kamelet +func NewKamelet(namespace string, name string) Kamelet { + return Kamelet{ + TypeMeta: metav1.TypeMeta{ + APIVersion: SchemeGroupVersion.String(), + Kind: KameletKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + } +} + +// NewKameletList creates a new list of kamelets +func NewKameletList() KameletList { + return KameletList{ + TypeMeta: metav1.TypeMeta{ + APIVersion: SchemeGroupVersion.String(), + Kind: KameletKind, + }, + } +} diff --git a/pkg/cmd/describe.go b/pkg/cmd/describe.go index 66b38f2..96a5ae7 100644 --- a/pkg/cmd/describe.go +++ b/pkg/cmd/describe.go @@ -85,6 +85,7 @@ func newCmdDescribe(rootCmdOptions *RootCmdOptions) *cobra.Command { cmd.AddCommand(cmdOnly(newDescribeKitCmd(rootCmdOptions))) cmd.AddCommand(cmdOnly(newDescribeIntegrationCmd(rootCmdOptions))) cmd.AddCommand(cmdOnly(newDescribePlatformCmd(rootCmdOptions))) + cmd.AddCommand(cmdOnly(newDescribeKameletCmd(rootCmdOptions))) return &cmd } diff --git a/pkg/cmd/describe_kamelet.go b/pkg/cmd/describe_kamelet.go new file mode 100644 index 0000000..7b1c8ff --- /dev/null +++ b/pkg/cmd/describe_kamelet.go @@ -0,0 +1,172 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You 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 cmd + +import ( + "errors" + "fmt" + "io" + "strings" + + "github.com/spf13/cobra" + + k8sclient "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" + "github.com/apache/camel-k/pkg/util/indentedwriter" +) + +func newDescribeKameletCmd(rootCmdOptions *RootCmdOptions) (*cobra.Command, *describeKameletCommandOptions) { + options := describeKameletCommandOptions{ + rootCmdOptions, + } + + cmd := cobra.Command{ + Use: "kamelet", + Aliases: []string{"kl"}, + Short: "Describe a Kamelet", + Long: `Describe a Kamelet.`, + PreRunE: decode(&options), + RunE: func(_ *cobra.Command, args []string) error { + if err := options.validate(args); err != nil { + return err + } + if err := options.run(args); err != nil { + fmt.Println(err.Error()) + } + + return nil + }, + } + + return &cmd, &options +} + +type describeKameletCommandOptions struct { + *RootCmdOptions +} + +func (command *describeKameletCommandOptions) validate(args []string) error { + if len(args) != 1 { + return errors.New("describe expects a Kamelet name argument") + } + return nil +} + +func (command *describeKameletCommandOptions) run(args []string) error { + c, err := command.GetCmdClient() + if err != nil { + return err + } + + kamelet := v1alpha1.NewKamelet(command.Namespace, args[0]) + kameletKey := k8sclient.ObjectKey{ + Namespace: command.Namespace, + Name: args[0], + } + + if err := c.Get(command.Context, kameletKey, &kamelet); err == nil { + fmt.Print(command.describeKamelet(kamelet)) + } else { + fmt.Printf("Kamelet '%s' does not exist.\n", args[0]) + } + + return nil +} + +func (command *describeKameletCommandOptions) describeKamelet(kamelet v1alpha1.Kamelet) (string, error) { + return indentedwriter.IndentedString(func(out io.Writer) error { + w := indentedwriter.NewWriter(out) + + describeObjectMeta(w, kamelet.ObjectMeta) + + w.Write(0, "Phase:\t%s\n", kamelet.Status.Phase) + + // Definition + def := kamelet.Spec.Definition + if def != nil { + w.Write(0, "Definition:\n") + w.Write(1, "Title:\t%s\n", def.Title) + w.Write(1, "Description:\n") + for _, s := range strings.Split(def.Description, "\n") { + w.Write(2, "%s\n", s) + } + if len(def.Required) > 0 { + w.Write(1, "Required:\n") + for _, req := range def.Required { + w.Write(2, "%s\n", req) + } + } + if len(def.Properties) > 0 { + w.Write(1, "Properties:\n") + for _, k := range kamelet.SortedDefinitionPropertiesKeys() { + p := def.Properties[k] + w.Write(2, "%s:\n", k) + w.Write(3, "Title:\t%s\n", p.Title) + w.Write(3, "Description:\t%s\n", p.Description) + w.Write(3, "Type:\t%s\n", p.Type) + if p.Default != nil { + w.Write(3, "Default:\t%s\n", p.Default) + } + } + } + } + + // Types + if len(kamelet.Spec.Types) > 0 { + w.Write(0, "Types:\n") + for _, k := range kamelet.SortedTypesKeys() { + t := kamelet.Spec.Types[k] + w.Write(1, "%s:\n", k) + w.Write(2, "Media Type: %s\n", t.MediaType) + } + } + + // Flow + // TODO pretty print flow data + flow := kamelet.Spec.Flow + if flow != nil { + w.Write(0, "Flow:\n") + w.Write(1, "%s\n", string(flow.RawMessage)) + } + + // Dependencies + if len(kamelet.Spec.Dependencies) > 0 { + w.Write(0, "Dependencies:\t\n") + for _, d := range kamelet.Spec.Dependencies { + w.Write(1, "%s\n", d) + } + } + + // Sources + if len(kamelet.Spec.Sources) > 0 { + w.Write(0, "Sources:\t\n") + w.Write(1, "Name\tLanguage\tCompression\tRef\tRef Key\n") + for _, s := range kamelet.Spec.Sources { + w.Write(1, "%s\t%s\t%t\t%s\t%s\n", + s.Name, + s.InferLanguage(), + s.Compression, + s.ContentRef, + s.ContentKey) + } + } + + return nil + }) +}